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);
ArgumentDescription
_sellerThe Ethereum address of the seller in this transaction.
_amountThe XNK amount to pay the seller. A value of 1 would equal 1e-18 XNK.
_metadataA Keccak-256 hash of the transaction's metadata.
Learn more about metadata
_policyThe 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
_mediatorThe Ethereum address of the mediator. Set a value of 0 to signify that this is a non-mediated transaction.
Learn more about mediators
_ownerThe 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;
ArgumentDescription
_idThe 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;
ArgumentDescription
_idThe 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 and TransactionConfirmed

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;
ArgumentDescription
_idThe id of the Ink Protocol transaction.

There are three states in which a buyer can confirm a transaction:

  1. 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.
  2. 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.
  3. 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's mediationExpiry 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;
ArgumentDescription
_idThe id of the Ink Protocol transaction.

Similar to buyer confirmations, there are three states where refundTransaction is available:

  1. 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.
  2. 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.
  3. 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's mediationExpiry 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;
ArgumentDescription
_idThe 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;
ArgumentDescription
_idThe 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;
ArgumentDescription
_idThe 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;
ArgumentDescription
_idThe 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;
ArgumentDescription
_idThe 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;
ArgumentDescription
_idThe id of the Ink Protocol transaction.
_buyerAmountThe XNK amount to give the buyer, before fees. A value of 1 would equal 1e-18 XNK.
Applies only to settleTransactionByMediator
_sellerAmountThe 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;
ArgumentDescription
_idThe id of the Ink Protocol transaction.
_ratingAn integer rating between 1 and 5 (inclusive).
_commentA 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.