Ink Protocol Transactions
Ink Protocol transactions fall into two categories, each with its own flow and set of states:
- Transactions without mediators
These transactions are very simple. The buyer pays and the seller accepts the payment, which instantly transfers the funds. - Transactions with mediators
These transactions can be disputed and a mediator can step in. Payment is escrowed until the transaction is confirmed. Mediators have the ability to take fees in most outcomes.
Below we will discuss the different states that Ink Protocol transactions can be in, as well as the functions that need to be called on the smart contract to transition into those states, and the resulting events that are emitted.
Ink Protocol uses events as the primary way to store data on the Ethereum blockchain. The smart contract itself is responsible for very little information, so events are extremely important for any app building on top of Ink Protocol.
Ink Protocol Transactions
All Ink Protocol transactions are stored on the smart contract as a mapping from the transaction id
to the Transaction
object:
// Mapping of all transactions by ID (globalTransactionId).
mapping(uint256 => Transaction) internal transactions;
A Transaction
is a struct that contains the minimum amount of transaction data that is needed for the smart contract logic. All other data is emitted through events.
// The struct definition for an Ink Transaction.
struct Transaction {
// The address of the buyer on the transaction.
address buyer;
// The address of the seller on the transaction.
address seller;
// The address of the policy contract for the transaction.
address policy;
// The address of the mediator contract for the transaction.
address mediator;
// The state of the transaction.
TransactionState state;
// The (block) time that the transaction transitioned to its current state.
// This value is only set for the states that need it to be set (states
// with an expiry involved).
uint256 stateTime;
// The XNK amount of the transaction.
uint256 amount;
}
Creating a Transaction
There are two functions named createTransaction
, and they differ only in the final argument, _owner
. Transactions are always created by the buyer, and therefore should be the msg.sender
for all createTransaction
calls.
function createTransaction(address _seller, uint256 _amount, bytes32 _metadata, address _policy, address _mediator, address _owner) external returns (uint256);
function createTransaction(address _seller, uint256 _amount, bytes32 _metadata, address _policy, address _mediator) external returns (uint256);
Argument | Description |
---|---|
_seller | The Ethereum address of the seller in this transaction. |
_amount | The XNK amount to pay the seller. A value of 1 would equal 1e-18 XNK. |
_metadata | A Keccak-256 hash of the transaction's metadata. Learn more about metadata |
_policy | The Ethereum address of the policy. A valid address is required when there is a mediator set. Use 0 when this transaction does not have a mediator.Learn more about policies |
_mediator | The Ethereum address of the mediator. Set a value of 0 to signify that this is a non-mediated transaction.Learn more about mediators |
_owner | The Ethereum address of the owner or marketplace. Excluding this argument creates a transaction that isn't tied to any owner. Learn more about owners |
On Succes
State:
Initiated
Event emitted:TransactionInitiated
The id
of the transaction can be found in the TransactionInitiated
event, and is also the return value of the createTransaction
call.
From here there are two possible next steps. The seller can choose to accept the transaction, or the buyer can choose to revoke the transaction.
Revoking a Tranasction
If the transaction was made in error, or if the buyer no longer wants to wait for the seller to accept it, the buyer may choose to revoke the payment, thus ending the transaction.
// msg.sender must be the buyer
function revokeTransaction(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
On Success
State:
Revoked
Event emitted:TransactionRevoked
When a transaction is revoked, the XNK is returned to the buyer, and no feedback can be left. No additional changes can be made to the transaction.
Accepting a Transaction
Once a transaction is in an Initiated
state, the seller can accept the transaction, thereby entering into an agreement to provide the goods or services detailed in the metadata at a set amount of XNK. Once a transaction is accepted, the buyer will ultimately be able to leave feedback for the seller, so sellers should only accept if they intend to deliver.
// msg.sender must be the seller
function acceptTransaction(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
On Success
For mediated transactions:
State:Accepted
Event emitted:TransactionAccepted
For non-mediated transactions:
State:Confirmed
Events emitted:TransactionAccepted
andTransactionConfirmed
For non-mediated transactions, the transaction is now complete, and the remaining action is just for the buyer to leave feedback.
For mediated transactions, the transaction is now in progress and is awaiting the seller's delivery of the good/service and the buyer's confirmation.
Heads up!
All functions, states, and events below apply only to mediated transactions.
Confirming a Transaction
Confirming a transaction represents the buyer acknowledging successful receipt of the good or service. This action transfers the payment out of escrow and to the seller. It cannot be undone, and it places the transaction in a final state where feedback can then be left.
// msg.sender must be the buyer
function confirmTransaction(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
There are three states in which a buyer can confirm a transaction:
Accepted
This represents a typical mediated transaction completing smoothly. The seller previously accepted the transaction, and the buyer is now acknowledging delivery, thereby transferring the payment to the seller.Disputed
After the buyer has initiated a dispute on a transaction, they can still confirm the transaction. For example, if the item was shipped and several days have passed, the buyer might've started a dispute. Then, days later, the package arrives, and the buyer decides to confirm the transaction. As long as the seller has not escalated the dispute to a mediator, the buyer can always cancel the dispute by confirming the transaction.Escalated
Once the seller has escalated the dispute, it falls into the hands of the assigned mediator. The smart contract allows the mediator a protected period of time to investigate and decide on an outcome without the ability for the buyer or seller to intervene. Once this period has expired, the buyer can choose to confirm the transaction. The duration is specified by the mediator contract'smediationExpiry
function.
On Success
From an
Accepted
state:
State:Confirmed
Event emitted:TransactionConfirmed
From a
Disputed
state:
State:ConfirmedAfterDispute
Event emitted:TransactionConfirmedAfterDispute
From an
Escalated
state:
State:ConfirmedAfterEscalation
Event emitted:TransactionConfirmedAfterEscalation
Refunding a Transaction
Whenever the buyer has the option to confirm a transaction, the seller also has the ability to refund the payment to the buyer. This puts the transaction in a final state, and the buyer will then have the ability to leave feedback on the transaction.
// msg.sender must be the seller
function refundTransaction(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
Similar to buyer confirmations, there are three states where refundTransaction
is available:
Accepted
If after accepting a transaction, the seller realizes that they are not able to deliver the good or service, they can refund the buyer.Disputed
If the buyer disputes a transaction, and the seller does not want to escalate it to a mediator, the seller can refund the transaction.Escalated
If instead the seller escalates the dispute, it falls into the hands of the assigned mediator. As discussed previously, the mediator has a protected period of time to respond to the dispute. Once this period has expired, the seller can choose to refund the transaction. The duration is specified by the mediator contract'smediationExpiry
function.
On Success
From an
Accepted
state:
State:Refunded
Event emitted:TransactionRefunded
From a
Disputed
state:
State:RefundedAfterDispute
Event emitted:TransactionRefundedAfterDispute
From an
Escalated
state:
State:RefundedAfterEscalation
Event emitted:TransactionRefundedAfterEscalation
Seller Confirming after Expiry
Once a seller has accepted a transaction, they typically deliver the good or service and await the seller's confirmation (which transfers the payment). Sometimes buyers may forget to confirm transactions, not fully realizing that they're withholding payment from the seller.
In these situations, after an amount of time specified in the policy has elapsed, the smart contract will allow the seller to unilaterally confirm the transaction. This transfers the payment and puts the transaction in a final state where the buyer can then leave feedback.
It may sound problematic to allow the seller to confirm the transaction, but the protection lies in the time period specified by the policy's transactionExpiry
function. The idea is that the seller has given the buyer sufficient time to either confirm the transaction or start a dispute. Exposing this ability to the seller is necessary to guarantee that transactions can always be completed as long as one party is active.
// msg.sender must be the seller
function confirmTransactionAfterExpiry(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
On Success
State:
ConfirmedAfterExpiry
Event emitted:TransactionConfirmedAfterExpiry
Disputing a Transaction
After a transaction is accepted, the seller is given a period of time to deliver the good or service before the buyer is able to initiate a dispute. This period of time is specified by the policy's fulfillmentExpiry
function. Once this period has expired, the buyer is able to initiate a dispute.
// msg.sender must be the buyer
function disputeTransaction(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
On Success
State:
Disputed
Event emitted:TransactionDisputed
Buyer Refunding after Expiry
After a dispute has been initiated, the buyer typically waits for the seller to either refund the payment or escalate the dispute to a mediator. In the event of a seller becoming unavailable (whether intentional or not), the smart contract allows the buyer to unilaterally trigger a refund. The payment is refunded, the transaction is put in a final state, and feedback can then be left.
Similar to the seller's ability to confirm a transaction in certain situations, it may seem strange that Ink Protocol allows buyers to force a refund. However, the protection is in the amount of time specified by the policy's escalationExpiry
function. The seller should have had sufficient time to either escalate the dispute or refund the transaction. Exposing this ability to the buyer is necessary to guarantee that transactions can always be completed as long as one party is active.
// msg.sender must be the buyer
function confirmTransactionAfterExpiry(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
On Success
State:
RefundedAfterExpiry
Event emitted:TransactionRefundedAfterExpiry
Escalating a Dispute
After a dispute is initiated by the buyer, the seller can escalate the dispute to the mediator. Once escalated, neither buyer nor seller can take action on the transaction for a period of time specified by the mediator's mediationExpiry
function.
// msg.sender must be the seller
function escalateDisputeToMediator(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
On Success
State:
Escalated
Event emitted:TransactionEscalated
Buyer/Seller Settling
Mediators have a protected period of time to determine the outcome of a dispute, based on their contract's mediationExpiry
function. After this time, the buyer, seller, and mediator all have the ability to conclude the dispute and complete the transaction.
The buyer has the ability to confirm the transaction and the seller can refund, both discussed previously. Alternatively, both buyer and seller have the additional option to unilaterally settle the dispute, which splits the amount 50/50 and transfers equivalent amounts to each party.
This option allows both parties to get a portion of the amount and conclude the transaction in the unlikely event of a mediator becoming unresponsive during an escalated dispute
// msg.sender can be either the buyer or seller
function settleTransaction(uint256 _id) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
On Success
State:
Settled
Event emitted:TransactionSettled
Mediator Actions
Once a dispute is escalated by the seller, the mediator steps in to help. There are three possible actions a mediator can take:
- Refund
Payment is returned to the buyer, less mediator fees. - Confirm
Payment is transferred to the seller, less mediator fees. - Settle
Payment is split (at mediator's discretion) between buyer and seller, less mediator fees.
// msg.sender must be the mediator contract
function refundTransactionByMediator(uint256 _id) external;
function confirmTransactionByMediator(uint256 _id) external;
function settleTransactionByMediator(uint256 _id, uint256 _buyerAmount, uint256 _sellerAmount) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
_buyerAmount | The XNK amount to give the buyer, before fees. A value of 1 would equal 1e-18 XNK. Applies only to settleTransactionByMediator |
_sellerAmount | The XNK amount to give the seller, before fees. A value of 1 would equal 1e-18 XNK. Applies only to settleTransactionByMediator |
Note that the _buyerAmount
and _sellerAmount
are before fees, so they should add up to the exact _amount
specified during transaction creation. The mediator's fees are deducted from the two amounts before being transferred to each party.
On Succes
Refund:
State:RefundedByMediator
Event emitted:TransactionRefundedByMediator
Confirm:
State:ConfirmedByMediator
Event emitted:TransactionConfirmedByMediator
Settle:
State:SettledByMediator
Event emitted:TransactionSettledByMediator
Providing Feedback
Once a transaction has reached a final state other than Revoked
, feedback can be left by the buyer. The list of eligible states is RefundedByMediator
, SettledByMediator
, ConfirmedByMediator
, Confirmed
, Refunded
, ConfirmedAfterExpiry
, ConfirmedAfterDispute
, RefundedAfterDispute
, RefundedAfterExpiry
, ConfirmedAfterEscalation
, RefundedAfterEscalation
, and Settled
.
// msg.sender must be the buyer
function provideTransactionFeedback(uint256 _id, uint8 _rating, bytes32 _comment) external;
Argument | Description |
---|---|
_id | The id of the Ink Protocol transaction. |
_rating | An integer rating between 1 and 5 (inclusive). |
_comment | A Keccak-256 hash of the comment. Learn more about feedback comments |
On Success
No change in state
Event emitted:FeedbackUpdated
The provideTransactionFeedback
function can be called multiple times by the buyer. Instead of updating or overwriting existing feedback, another feedback is simply written. When counting or displaying feedback, only the latest feedback on each transaction should be counted. This allows the contract to keep an untouched history of events.
Updated over 5 years ago