Skip to main content

Self-Sponsoring Shimmer 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 Shimmer 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.

This is particularly useful for Shimmer assets because the Move objects derived from Shimmer Stardust Outputs don't contain IOTA coins. As a result, addresses holding these objects lack the IOTA coins necessary to pay for gas.

Claim a Shimmer Basic Output with Self-Sponsorship

1. Identify the Self-Sponsoring Address

Shimmer uses a different coin_type (4219) than IOTA (4218). To find the self-sponsoring address, use the IOTA coin_type.

    // For this example we need to derive addresses that are not at different
// indexes and coin_types, one for sponsoring with IOTA coin type and one for
// claiming the Basic Output with Shimmer 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'/{SHIMMER_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 Shimmer address. This process is similar to the one outlined in the Basic Output guide.

    let basic_output_object_ref = basic_output_object.object_ref();

// Create a PTB to for claiming the assets of a basic output for the sender
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 coming from the Shimmer network, i.e., the
// SMR coin
let type_arguments = vec![SMR::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 succesful and will
// return a `base_token` (i.e., SMR) 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 SMR balance
// Type argument for the SMR coin
let type_arguments = vec![SMR::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)
}

3. Sign the Transaction

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

        .ok_or(anyhow!("No coins found for sponsor"))?;

// 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),