zkSend SDK
Creating zkSend Links

Creating zkSend Links

⚠️

Products and services that incorporate zkSend links may be subject to financial regulations, including obtaining money transmitter licenses in jurisdictions where you provide your services. It is the developer's responsibility to ensure compliance with all relevant laws and obtain any necessary licenses. The information provided by Mysten Labs is not legal advice, and developers should consult with a qualified legal professional to address their specific circumstances.

Limitations

  • zkSend only supports Mainnet at this time.
  • Objects within links must be publicly transferrable.

Create a link

You can start creating your own zkSend link using the ZkSendLinkBuilder class. This class constructor takes an object with the following options:

  • sender (required) - Required. The address of the sender / creator of the link.
  • redirect (optional) - The redirect configuration for the link. See "Claim redirect" below for more information.
    • url - The url to redirect to after the link have been claimed.
    • name - The name of your dApp. This will be shown to the user when claiming the link.
  • client (optional) - The @mysten/sui.js client used to fetch data to construct the link. If not provided, a default client will be used.
import { ZkSendLinkBuilder } from '@mysten/zksend';
 
const link = new ZkSendLinkBuilder({
	sender: '0x...',
});

Adding SUI to the link

You can add SUI to the link by calling link.addClaimableMist(). This method takes the following params:

  • amount (required) - The amount of MIST (the base unit of SUI) to add to the link.

Adding non-SUI coins to the link

You can add non-SUI coins to the link by calling link.addClaimableBalance(). This method takes the following params:

  • coinType (required) - The coin type of the coin to add to the link (e.g. 0x2::sui::SUI).
  • amount (required) - The amount of the coin to add to the link. Represented in the base unit of the coin.

The SDK will automatically perform the necessary coin management logic to transfer the defined amount, such as merging and splitting coin objects.

Adding objects to the link

You can add a publicly-transferrable object to the link by calling link.addClaimableObject(). This method takes the following params:

  • id (required) - The ID of the object. This must be owned by the sender you configured when creating the link.

Adding objects created in the same transaction

You can create objects to add to links in the same transaction the link is created in by using link.addClaimableObjectRef()

  • ref (required) - The reference to the object you want to add to the link.
  • type (required) - The move type of the object you are adding
const txb = new TransactionBlock();
 
const link = new ZkSendLinkBuilder({
	sender: '0x...',
});
 
const newObject = txb.moveCall({
	target: `${PACKAGE_ID}::your_module::mint`,
});
 
link.addClaimableObjectRef({
	ref: newObject,
	type: `${PACKAGE_ID}::your_module::YourType`,
});
 
// Adds the link creation transactions to the transaction block
link.createSendTransaction({
	transactionBlock: txb,
});

Getting the link URL

At any time, you can get the URL for the link by calling link.getLink().

Submitting the link transaction

Once you have built your zkSend link, you need to execute a transaction to transfer assets and make the link claimable.

You can call the link.createSendTransaction() method, which returns a TransactionBlock object that you can sign and submit to the blockchain.

const txb = await link.createSendTransaction();
 
const { bytes, signature } = txb.sign({ client, signer: keypair });
 
const result = await client.executeTransactionBlock({
	transactionBlock: bytes,
	signature,
});

If you have a keypair you would like to send the transaction with, you can use the create method as shorthand for creating the send transaction, signing it, and submitting it to the blockchain.

await link.create({
	signer: yourKeypair,
});

Claim redirect

You can configure a redirect for the link, which will redirect the user to a URL of your choice after the link has been claimed. This is useful for dApps that want to use zkSend / Stashed as an onboarding tool, and have the user redirected to their dApp after claiming the link.

To configure a redirect, you need to provide a redirect object when creating the link. This object takes the following params:

  • url - The URL to redirect to after the link has been claimed.
  • name - The name of your dApp. This will be shown to the user when claiming the link.

When redirecting to the URL, if the user claims with zkLogin, then Stashed will automatically add a stashed_address query parameter to the URL, containing the Sui address of the user that claimed the asset. The Stashed wallet will automatically read this query parameter, and consider itself connected as a result of a claim redirect.

import { ZkSendLinkBuilder } from '@mysten/zksend';
 
const link = new ZkSendLinkBuilder({
	sender: '0x...',
	redirect: {
		url: 'https://your-dapp.com',
		name: 'Your dApp',
	},
});

Claiming a link

To claim a link via the SDK you can use the ZkSendLink class:

import { ZkSendLink } from '@mysten/zksend';
 
// create a link instance from a URL
const link = await ZkSendLink.fromUrl('https://zksend.com/claim#$abc...');
 
// list what claimable assets the link has
const { nfts, balances } = link.assets;
 
// claim all the assets from the link
await link.claimAssets(addressOfClaimer);

Listing links you have created

To list the links created by a specific address, you can use the listCreatedLinks function:

import { listCreatedLinks } from '@mysten/zksend';
 
const { links, hasNextPage, cursor } = await listCreatedLinks({
	address: addressOfCreator,
});
 
// get the claimable assets for this link (will be empty if the link has been claimed)
const { nfts, balances } = await links[0].assets;

Listing transactions and the links they created

getSentTransactionBlocksWithLinks will return a list of transaction blocks sent by the provided address. Each result will include the transaction that was sent, along with an array containing any links that were created or regenerated by that transaction block.

import { getSentTransactionBlocksWithLinks } from '@mysten/zksend';
 
const { data, hasNextPage, nextCursor } = await getSentTransactionBlocksWithLinks({
	address: addressOfCreator,
});
 
for (const { transactionBlock, links } of data) {
	// get the claimable assets for this link (will be empty if the link has been claimed)
	const firstLink = links[0];
 
	// link is claimed
	firstLink.claimed;
	const { nfts, balances } = firstLink.assets;
 
	// claim link
	await firstLink.link.claimAssets(addressOfClaimer);
}

By default getSentTransactionBlocksWithLinks will not load the assets for claimed links. This can be changed by passing loadClaimedAssets: true to the function.

import { getSentTransactionBlocksWithLinks } from '@mysten/zksend';
 
const { data, hasNextPage, nextCursor } = await getSentTransactionBlocksWithLinks({
	address: addressOfCreator,
	loadClaimedAssets: true,
});

Regenerating links

If you lose a link you've created, you can re-generate the link (this can only done from the address that originally created the link):

import { listCreatedLinks } from '@mysten/zksend';
 
const { links, hasNextPage, cursor } = await listCreatedLinks({
	address: addressOfCreator,
});
 
// url will be the new link url
const { url, transactionBlock } = await links[0].link.createRegenerateTransaction(
	addressOfLinkCreator,
);
 
// Execute the transaction block to regenerate the link
await client.signAndExecuteTransactionBlock({
	transactionBlock,
	signer: keypair,
});

Bulk link creation

To create multiple links in a single transaction block, you can use ZkSendLinkBuilder.createLinks:

const links = [];
 
for (let i = 0; i < 10; i++) {
	const link = new ZkSendLinkBuilder({
		client,
		sender: keypair.toSuiAddress(),
	});
 
	link.addClaimableMist(100n);
	links.push(link);
}
 
const urls = links.map((link) => link.getLink());
 
const txb = await ZkSendLinkBuilder.createLinks({
	links,
});
 
await client.signAndExecuteTransactionBlock({
	transactionBlock: txb,
	signer: keypair,
});