Transaction sponsoring

In Unique Network, transactions can be sponsored, allowing for a gasless experience where the sponsor covers the transaction fees. This enables seamless and cost-free transfers of NFTs and the execution of smart contracts, even for accounts without native tokens.

Collection sponsoring

Let's start with a full code example and then break it down.

import { Sdk, CHAIN_CONFIG } from "@unique-nft/sdk/full";
import { Sr25519Account } from "@unique-nft/sdk/sr25519";

const main = async () => {
  // 0. Initialize accounts
  // Set up sponsor and empty accounts
  const sponsorMnemonic = "...";
  const sponsor = Sr25519Account.fromUri(sponsorMnemonic);
  const emptyAccount = Sr25519Account.fromUri(
    Sr25519Account.generateMnemonic(),
  );

  // 0.1. Initialize SDK with sponsor
  // Set SDK with sponsor as default signer
  const sdk = new Sdk({
    baseUrl: CHAIN_CONFIG.opal.restUrl,
    account: sponsor,
  });

  // 1. Sponsor creates a collection
  // Create a new collection by sponsor
  const collectionResponse = await sdk.collection.create({
    name: "Sponsored",
    description: "This collection is sponsored",
    tokenPrefix: "SC",
  });

  const { collectionId } = collectionResponse.parsed!;

  // 2. Create NFT
  // Create an NFT in the collection
  console.log("Creating NFT");
  const nftResponse = await sdk.token.create({ collectionId });

  const nft = nftResponse.parsed!;

  // 3. Set sponsorship for the collection
  // Make transactions for the collection gasless

  // 3.1. Set sponsor address
  // Set sponsor address for collection transactions
  await sdk.collection.setSponsorship({
    collectionId,
    newSponsor: sponsor.address,
  });

  // 3.2. Confirm sponsorship
  // Sponsor confirms sponsorship
  await sdk.collection.confirmSponsorship({ collectionId });

  // 3.3. Set sponsorship limits
  // Set limits for sponsorship; sponsor all transfers
  await sdk.collection.setLimits({
    collectionId,
    limits: { sponsorTransferTimeout: 0 },
  });

  // 4. Test sponsorship
  // Test the sponsorship functionality

  // 4.1. Sponsor transfers NFT to emptyAccount
  // Sponsor transfers NFT to empty account
  await sdk.token.transfer({ ...nft, to: emptyAccount.address });

  {
    // 4.2. Check new owner
    // Verify the empty account owns the NFT
    const { owner } = await sdk.token.owner(nft);
    if (owner !== emptyAccount.address)
      throw Error("Empty account is not the owner of NFT");
  }

  // 4.3. Empty account transfers token
  // Empty account transfers NFT, gas paid by sponsor
  const { fee } = await sdk.token.transfer(
    { ...nft, to: sponsor.address, address: emptyAccount.address },
    { signer: emptyAccount.signer },
  );
  console.log("Gasless transfer fee:", fee?.raw);

  // 4.4. Check NFT owner is sponsor again
  // Verify sponsor owns the NFT again
  console.log("Check NFT owner is sponsor again");
  const { owner } = await sdk.token.owner(nft);
  if (owner !== sponsor.address) throw Error("Sponsor is not the owner of NFT");
};

main();

0. Initialize Accounts and SDK

Start by setting up the necessary accounts. The sponsor account must have a balance of native tokens to cover transaction fees, while the empty account will perform gasless transfers.

Initialize the SDK with the sponsor account set as the default signer.

// 0. Initialize accounts
// Set up sponsor and empty accounts
const sponsorMnemonic = "...";
const sponsor = Sr25519Account.fromUri(sponsorMnemonic);
const emptyAccount = Sr25519Account.fromUri(
  Sr25519Account.generateMnemonic(),
);

// 0.1. Initialize SDK with sponsor
// Set SDK with sponsor as default signer
const sdk = new Sdk({
  baseUrl: CHAIN_CONFIG.opal.restUrl,
  account: sponsor,
});

1. Create Collection and NFT

The sponsor creates a new NFT collection and a token on its own address.

// 1. Sponsor creates a collection
// Create a new collection by sponsor
const collectionResponse = await sdk.collection.create({
  name: "Sponsored",
  description: "This collection is sponsored",
  tokenPrefix: "SC",
});

const { collectionId } = collectionResponse.parsed!;

// 2. Create NFT
// Create an NFT in the collection
console.log("Creating NFT");
const nftResponse = await sdk.token.create({ collectionId });

const nft = nftResponse.parsed!;

2. Set Sponsorship for the Collection

Configure the collection to allow gasless transactions, meaning the sponsor will cover the transaction fees.

// 3. Set sponsorship for the collection
// Make transactions for the collection gasless

// 3.1. Set sponsor address
// Set sponsor address for collection transactions
await sdk.collection.setSponsorship({
  collectionId,
  newSponsor: sponsor.address,
});

Now the sponsor we set on a previous step should confirms their sponsorship for the collection.

  // 3.2. Confirm sponsorship
  // Sponsor confirms sponsorship
  await sdk.collection.confirmSponsorship({ collectionId });

By default, there is a cooldown period of five blocks between transactions. During this period, transaction fees are withdrawn from transaction signers as usual.

Let's define the limits for sponsorship to ensure the sponsor covers the fees for all transfers within the collection.

// 3.3. Set sponsorship limits
// Set limits for sponsorship; sponsor all transfers
await sdk.collection.setLimits({
  collectionId,
  limits: { sponsorTransferTimeout: 0 },
});

3. Test sponsorship

Now let's test it. Send the previously created NFT to the empty account. After that, the empty account will send the NFT back even though it does not have any native tokens. The transaction fees will be withdrawn from the sponsor's balance.

// 4. Test sponsorship
// Test the sponsorship functionality

// 4.1. Sponsor transfers NFT to emptyAccount
// Sponsor transfers NFT to empty account
await sdk.token.transfer({ ...nft, to: emptyAccount.address });

{
  // 4.2. Check new owner
  // Verify the empty account owns the NFT
  const { owner } = await sdk.token.owner(nft);
  if (owner !== emptyAccount.address)
    throw Error("Empty account is not the owner of NFT");
}

// 4.3. Empty account transfers token
// Empty account transfers NFT, gas paid by sponsor
const { fee } = await sdk.token.transfer(
  { ...nft, to: sponsor.address, address: emptyAccount.address },
  { signer: emptyAccount.signer },
);
console.log("Gasless transfer fee:", fee?.raw);

// 4.4. Check NFT owner is sponsor again
// Verify sponsor owns the NFT again
console.log("Check NFT owner is sponsor again");
const { owner } = await sdk.token.owner(nft);
if (owner !== sponsor.address) throw Error("Sponsor is not the owner of NFT");

Contract sponsoring

Review the EVM section for contract sponsorship details.