Skip to main content

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:

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:

IdentifierIs
senderthe account that authors the transfer and gives up the 1,000 KMOI
payerthe account that pays the fuel - the sponsor
beneficiarythe 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);
note

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:

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);

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:

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);

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

SymptomLikely cause
Payer signature is missing.payer is set but its signature was not passed to sendInteraction.
Payer must be a participant accountpayer is a Logic or Asset ID.
Signatures are present but the network rejects themThe co-signers signed a different object. Finalize ixObject before collecting any signatures.
Insufficient balance although the sender is fundedFuel comes out of the payer's balance.

Where Next