Skip to main content

2-Way Payments

The problem in every blockchain (other than AIBlock 😉) is that Alice pays Bob some coins, but the chain never records why. Put another way, there's no record of trade where Alice pays Bob for an item, and Bob sends Alice this item. Instead, payments on blockchain just look like charity, as though Alice felt sorry for Bob and decided to send him some coin for coffee (or maybe cocaine, the chain doesn't say). The closest you can currently get is to create a smart contract that encapsulates this logic, but those take far too long to write and are prone to risky and expensive bugs.

2-Way payments solve this problem. Alice can send Bob 10 tokens, and Bob can send Alice 1 item, and both events are recorded on the AIBlock chain with no smart contracts, no fuss!


Send a Payment for an Item​

2-Way transactions on AIBlock are atomic, meaning that either both Alice and Bob get what they want, or the entire transaction is canceled. But they're also async, meaning that Alice and Bob don't need to be online at the same time in order for the transaction to go through. Let's test this as Alice, wanting to make a 10 token payment to Bob for 1 of his on-chain items

import { initIAssetToken, initIAssetItem, Wallet } from '@2waychain/2wayjs';

// ... wallet initialization set up

// The tokens we want to send
const tokensToSend = {"Token": 10};

// The item we want to receive
const itemAliceWants = {
"Item": {
"amount": 10, // The amount of the item we want
"drs_tx_hash": "default_drs_tx_hash" // The genesis transaction hash for the item, its identifier on-chain
}};

// Make the 2-way payment as Alice
const paymentResult = await wallet.make2WayPayment(
bobAddress, // Payment address
tokensToSend, // Payment asset
itemAliceWants, // Receiving asset
allKeypairs, // Alice's keypairs
aliceAddress, // Receive address
);

This will complete Alice's side of the trade. NOTE: Alice will need to initialize her wallet with a Valence node to connect to. The Valence node is what allows this transaction to be asynchronous, because Bob will retrieve the transaction information from the Valence node before he can agree to the payment terms. This also means that Bob must be connected to the same Valence node in order to see the 2-way transaction from his side.

If Alice wants to save the response information from this 2-Way transaction, she can retrieve the DRUID and encrypted transaction values for storage as the result of the make2WayPayment call:

const { druid, encryptedTx } = paymentResult.content.make2WayPaymentResponse;

The DRUID is the string value that Alice and Bob agree on so that their trades can be matched on the blockchain. It's generated by 2Way.js automatically, so you don't have to think about this too much, but this value can be absolutely anything. So long as both Alice and Bob use the same DRUID in their 2-Way transactions, the transactions will be matched by the chain successfully.


Fetch Pending 2-Way Payments​

If Bob is connected to the same Valence node as Alice, he can retrieve her pending 2-Way payment and see if he likes the terms of the trade or not:

// Fetch pending 2-Way payments
const pending2WayPaymentsResult = await wallet.fetchPending2WayPayments(
allKeypairs, // All of Bob's keypairs, which the function will pull addresses from
allEncryptedTxs, // Encrypted transactions, which are only required if they've been saved by Bob before
);

const pending2WayPayments: IResponseIntercom<IPending2WayTxDetails> = pending2WayPaymentsResult.content.fetchPending2WayPaymentsResponse;

The result of this call might look something like the following:

{
"2a646...f8b98": {
"timestamp": 1655117144145,
"value": {
"druid": "DRUID0xd0f407436f7f1fc494d7aee22939090e",
"senderExpectation": {
"from": "",
"to": "2a646...f8b98",
"asset": {
"Item": {
"amount": 10,
"drs_tx_hash": "default_drs_tx_hash"
}
}
},
"receiverExpectation": {
"from": "295b2...8d4fa",
"to": "18f70...caeda",
"asset": {
"Token": 25200
}
},
"status": "pending",
"computeHost": "http://127.0.0.1:3003"
}
}
}

Here we can see all kinds of information about the proposed 2-Way payment, including the DRUID, the item or asset that Alice expects in return from Bob, and the status of the transaction (in this case, "pending").

If Bob has the item that Alice wants, he now has 2 options: he can either accept the terms, in which case the transaction will be executed, or reject them, in which case nothing happens and Alice and Bob both keep their respective assets.


Accepting and Rejecting 2-Way Payments​

2Way.js provides built-in ways to accept and reject 2-Way payments, assuming that both parties are coordinating off the same Valence node.

const druid = 'DRUID0xd0f407436f7f1fc494d7aee22939090e';    // DRUID value that matches Alice and Bob's transactions
const pending2WTransactions = pending2WayTransactionsResult // The response we received from fetching pending 2-Way payments
.content
.fetchPending2WayPaymentsResponse;

// Accept a 2-Way payment using its unique `DRUID` identifier
await wallet.accept2WayPayment(druid, pending2WayTransactions, allKeypairs);

//-- --------------------------------- OR ---------------------------------- --//

// Reject a 2-Way payment using its unique `DRUID` identifier
await wallet.reject2WayPayment(druid, pending2WayTransactions, allKeypairs);

Once called, 2Way.js will automatically send the right response to the Valence node, and the entire 2-Way transaction can be executed on the AIBlock chain if accepted. Once executed on-chain, both Alice and Bob will see the balances of their respective assets updated to reflect the trade.