Sponsoring an Interaction in MOI
By default the sender of an Interaction pays its fuel. The optional payer field hands that bill to a different participant, so an application can absorb the cost of its users' Interactions. Since the payer's balance is spent, the payer must co-sign the Interaction.
This tutorial sponsors a single KMOI transfer: the sender authors it, the beneficiary receives the asset, and a third account pays the fuel.
Prerequisites
We recommend reading:
- Interactions - the
payerfield and execution fuel - Submitting an Interaction - the signing and submission flow
- Create Assets - transferring an asset between participants
The walkthrough assumes your code already has a provider and initialized wallet signers, as set up in Setting up JS-MOI-SDK.
The Scenario
Three participants take part in a single KMOI transfer. The sender moves 1,000 KMOI to the beneficiary, and the payer picks up the fuel bill:
| Identifier | Is |
|---|---|
sender | the account that authors the transfer and gives up the 1,000 KMOI |
payer | the account that pays the fuel - the sponsor |
beneficiary | the account that receives the 1,000 KMOI |
Each has its own wallet:
import { MASNAssetLogic, KMOI_ASSET_ID } from "js-moi-sdk";
const participant = async (wallet) => ({
id: (await wallet.getIdentifier()).toHex(),
key_id: await wallet.getKeyId(),
sequence: await wallet.getNonce(),
});
const sender = await participant(senderWallet);
const payer = await participant(payerWallet);
const beneficiary = await participant(beneficiaryWallet);
const balance = async (id) => await provider.getBalance(id, KMOI_ASSET_ID);
Only a participant account can be a payer. The SDK refuses a Logic ID or Asset ID in the payer field before anything is sent.
Sponsoring a Transfer
Step 1: Build the Interaction with a Payer
Build the transfer as usual, naming the payer:
- Code
- Output
const kmoi = new MASNAssetLogic(senderWallet);
const ixObject = await kmoi.transfer(beneficiary.id, 1_000).payer(payer.id).ixData();
console.log("payer: ", ixObject.payer);
console.log("participants: ", ixObject.participants);
// Console Output
payer: 0x00000000defdf03dd126c5b4d80c433c773975f52134264453c8393d00000000
participants: [
{
id: '0x00000000e2253583b559663a6af90b29ebb02118f1553d3844bb74aa00000000', # benficiary-id
lock_type: 0
},
{
id: '0x1080fffe4cd973c4eb83cdb8870c0de209736270491b7acc99873da100000000', # asset-id
lock_type: 2
}
]
The SDK declares the beneficiary (MutateLock) and the KMOI asset (NoLock) as participants; the sender is implicit. No other participant is declared as a notary, so the payer is the only co-signer required alongside the sender.
Step 2: Co-sign as the Payer and Submit
The payer signs the same object with its own wallet, and the sender submits with both signatures:
- Code
- Output
const sponsoredTransfer = async () => {
const sigAlgo = senderWallet.signingAlgorithms["ecdsa_secp256k1"];
const payerSignatures = await payerWallet.signRawInteractionObject(
ixObject,
sigAlgo,
);
const response = await senderWallet.sendInteraction(
ixObject,
payerSignatures,
);
console.log("ix hash:", response.hash);
const receipt = await response.wait();
console.log("status: ", receipt.status, " fuel used:", receipt.fuel_used);
};
const senderBefore = await balance(sender.id);
const payerBefore = await balance(payer.id);
const beneficiaryBefore = await balance(beneficiary.id);
await sponsoredTransfer();
const senderAfter = await balance(sender.id);
const payerAfter = await balance(payer.id);
const beneficiaryAfter = await balance(beneficiary.id);
console.log("delta sender:", senderAfter - senderBefore);
console.log("delta payer:", payerAfter - payerBefore);
console.log("delta beneficiary: +", beneficiaryAfter - beneficiaryBefore);
// Console Output
ix hash: 0x6c3a96f2b705b39ceb644552bea998c764545282d7f8733fcc13eb02fe878942
status: 0 fuel used: 0x12b
delta sender: -1000
delta payer: -14950
delta beneficiary: +1000
The sender paid only the 1,000 KMOI transferred. The payer paid the fuel: 0x12b (299) units × the default fuel_price of 50 = 14,950 KMOI. Without payer, the sender's delta would have been -15,950.
Troubleshooting
| Symptom | Likely cause |
|---|---|
Payer signature is missing. | payer is set but its signature was not passed to sendInteraction. |
Payer must be a participant account | payer is a Logic or Asset ID. |
| Signatures are present but the network rejects them | The co-signers signed a different object. Finalize ixObject before collecting any signatures. |
| Insufficient balance although the sender is funded | Fuel comes out of the payer's balance. |
Where Next
- Interactions - how fuel is priced and who is charged.
- Validation Constraints - the signature and weight rules every Interaction must meet.
- Multi-Sig Interactions - requiring notary participants to co-sign, which composes with a payer.
- Create Participants - registering keys and their weights on a participant.