Self-Sponsoring IOTA Assets Claims
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.
- TypeScript
- Rust
    // 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'"
    // 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.
    let sponsor_derivation_path =
        DerivationPath::from_str(format!("m/44'/{IOTA_COIN_TYPE}'/0'/0'/5'").as_str())?;
    let sender_derivation_path =
        DerivationPath::from_str(format!("m/44'/{IOTA_COIN_TYPE}'/0'/0'/50'").as_str())?;
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.
- TypeScript
- Rust
    // 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));
    // Create a PTB to claim the assets related to the basic output.
    let pt = {
        // Init the builder
        let mut builder = ProgrammableTransactionBuilder::new();
        ////// Command #1: extract the base token and native tokens bag.
        // Type argument for a Basic Output holding IOTA coin
        let type_arguments = vec![GAS::type_tag()];
        // Then pass the basic output object as input
        let arguments = vec![builder.obj(ObjectArg::ImmOrOwnedObject(basic_output_object_ref))?];
        // Finally call the basic_output::extract_assets function
        if let Argument::Result(extracted_assets) = builder.programmable_move_call(
            STARDUST_ADDRESS.into(),
            ident_str!("basic_output").to_owned(),
            ident_str!("extract_assets").to_owned(),
            type_arguments,
            arguments,
        ) {
            // 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
            let extracted_base_token = Argument::NestedResult(extracted_assets, 0);
            let extracted_native_tokens_bag = Argument::NestedResult(extracted_assets, 1);
            ////// Command #2: delete the empty native tokens bag
            let arguments = vec![extracted_native_tokens_bag];
            builder.programmable_move_call(
                IOTA_FRAMEWORK_ADDRESS.into(),
                ident_str!("bag").to_owned(),
                ident_str!("destroy_empty").to_owned(),
                vec![],
                arguments,
            );
            ////// Command #3: create a coin from the extracted IOTA balance
            // Type argument for the IOTA coin
            let type_arguments = vec![GAS::type_tag()];
            let arguments = vec![extracted_base_token];
            let new_iota_coin = builder.programmable_move_call(
                IOTA_FRAMEWORK_ADDRESS.into(),
                ident_str!("coin").to_owned(),
                ident_str!("from_balance").to_owned(),
                type_arguments,
                arguments,
            );
            ////// Command #5: send back the base token coin to the user.
            builder.transfer_arg(sender, new_iota_coin)
        }
        builder.finish()
    };
3. Sign the Transaction
For this transaction, both the sender address (the object's owner) and the sponsor address must sign the transaction.
- TypeScript
- Rust
    // 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}`);
    // Create the transaction data that will be sent to the network and allow
    // sponsoring
    let tx_data = TransactionData::new_programmable_allow_sponsor(
        sender,
        vec![gas_coin.object_ref()],
        pt,
        gas_budget,
        gas_price,
        sponsor,
    );
    // Sender signs the transaction
    let sender_signature = keystore.sign_secure(&sender, &tx_data, Intent::iota_transaction())?;
    // Sponsor signs the transaction
    let sponsor_signature = keystore.sign_secure(&sponsor, &tx_data, Intent::iota_transaction())?;
    // Execute transaction; the transaction data is created using the signature of
    // the sender and of the sponsor.
    let transaction_response = iota_client
        .quorum_driver_api()
        .execute_transaction_block(
            Transaction::from_data(tx_data, vec![sender_signature, sponsor_signature]),
            IotaTransactionBlockResponseOptions::full_content(),
            Some(ExecuteTransactionRequestType::WaitForLocalExecution),
        )
        .await?;
    println!("Transaction digest: {}", transaction_response.digest);