Skip to main content

Self-Sponsoring Iota Assets Claims

Exchanges and dApp Devs Only

The migration documentation describes the processes needed to claim and migrate output types manually; However, for the average user, this knowledge is not needed and is abstracted in the wallet web application (dashboard). The specific migration knowledge described here is unnecessary for people using a regular wallet.

If you own Iota assets but don't have any IOTA coins to cover transaction fees, self-sponsorship can help you claim those assets. In this process, a sponsor (an IOTA address) pays the transaction gas fees for another address (the user). When claiming assets, a sponsor can cover the gas fees required for the transaction.

Claim an Iota Basic Output with Self-Sponsorship

1. Identify the Self-Sponsoring Address

Use the IOTA coin_type to derive the sponsor and sender addresses.

    // For this example we need to derive addresses that are at different
// indexes and coin_types, one for sponsoring with IOTA coin type and one for
// claiming the Basic Output with Iota coin type.
const sponsorDerivationPath = "m/44'/4218'/0'/0'/5'"
const senderDerivationPath = "m/44'/4218'/0'/0'/50'"

2. Create the PTB for Claiming

Next, create a Programmable Transaction Block (PTB) to claim a BasicOutput owned by the derived Iota address. This process is similar to the one outlined in the Basic Output guide.

    // Create a PTB to claim the assets related to the basic output.
const tx = new Transaction();
// Extract the base token and native tokens bag.
// Type argument for a Basic Output holding IOTA coin.
const gasTypeTag = "0x2::iota::IOTA";
// Then pass the basic output object as input.
const args = [tx.object(basicOutputObjectId)];
// Finally call the basic_output::extract_assets function.
const extractedBasicOutputAssets = tx.moveCall({
target: `${STARDUST_PACKAGE_ID}::basic_output::extract_assets`,
typeArguments: [gasTypeTag],
arguments: args,
});

// If the basic output can be unlocked, the command will be successful and will
// return a `base_token` balance and a `Bag` of native tokens.
const extractedBaseToken = extractedBasicOutputAssets[0];
const extractedNativeTokensBag = extractedBasicOutputAssets[1];

// Delete the empty native tokens bag.
tx.moveCall({
target: `0x2::bag::destroy_empty`,
typeArguments: [],
arguments: [extractedNativeTokensBag],
});

// Create a coin from the extracted IOTA balance.
const iotaCoin = tx.moveCall({
target: '0x2::coin::from_balance',
typeArguments: [gasTypeTag],
arguments: [extractedBaseToken],
});

// Send back the base token coin to the user.
tx.transferObjects([iotaCoin], tx.pure.address(sender));

3. Sign the Transaction

For this transaction, both the sender address (the object's owner) and the sponsor address must sign the transaction.

    // Get a gas coin belonging to the sponsor.
const sponsorGasObjects = await iotaClient.getCoins({ owner: sponsor });
const sponsorGasCoin = sponsorGasObjects.data?.[0];

if (!sponsorGasCoin) {
throw new Error('No coins found for sponsor');
}

tx.setSender(sender);
tx.setGasOwner(sponsor);
// Set sponsor’s gas object to cover fees.
tx.setGasPayment([{
objectId: sponsorGasCoin.coinObjectId,
version: sponsorGasCoin.version,
digest: sponsorGasCoin.digest
}]);
tx.setGasBudget(10_000_000);

// Sign the transaction with the sponsor and sender keypairs.
const sponsorSignedTransaction = await tx.sign({ client: iotaClient, signer: sponsorKeypair });
const senderSignedTransaction = await tx.sign({ client: iotaClient, signer: senderKeypair });

// Build the transaction and execute it.
const builtTransaction = await tx.build({ client: iotaClient });
const result = await iotaClient.executeTransactionBlock({
transactionBlock: builtTransaction,
signature: [sponsorSignedTransaction.signature, senderSignedTransaction.signature]
});

// Get the response of the transaction.
const response = await iotaClient.waitForTransaction({ digest: result.digest });
console.log(`Transaction digest: ${response.digest}`);