llms.txt
@mysten/sui v2.0 and a new dApp Kit are here! Check out the migration guide
Mysten Labs SDKs
Transactions

Allowances

Create allowances and spend from another account's address balance

An allowance lets a spender use a funder's address balance, subject to spending limits. The spender signs transactions that use it. The network must have allowance support enabled (protocol v137 or later).

Creating an allowance

This non-app allowance permits up to 100 USDC of spending over 24 hours. funder is the signer creating the allowance. Replace 0xUsdcPackage::usdc::USDC with your network's USDC type; amounts use its six decimal places.

import { Transaction } from '@mysten/sui/transactions';

const USDC = '0xUsdcPackage::usdc::USDC';
const balanceType = `0x2::balance::Balance<${USDC}>`;
const expirationMs = BigInt(Date.now() + 24 * 60 * 60 * 1000);
const tx = new Transaction();

// RateLimit is a Move type, so construct its empty option with a Move call.
const noRateLimit = tx.moveCall({
	target: '0x1::option::none',
	typeArguments: ['0x2::allowance::RateLimit'],
});

tx.moveCall({
	target: '0x2::allowance::new',
	typeArguments: [balanceType],
	arguments: [
		tx.pure.string('USDC spending allowance'),
		tx.pure.address(spenderAddress),
		tx.pure.option('u256', 100_000_000n), // Lifetime cap: 100 USDC
		tx.pure.option('u64', null), // No delayed start
		tx.pure.option('u64', expirationMs),
		noRateLimit,
	],
});

const result = await client.core.signAndExecuteTransaction({
	transaction: tx,
	signer: funder,
	include: { effects: true },
});

if (result.$kind === 'FailedTransaction') {
	throw new Error(result.FailedTransaction.status.error?.message ?? 'Allowance creation failed');
}

// This transaction creates one shared object: the allowance.
const allowance = result.Transaction.effects!.changedObjects.find(
	(change) => change.idOperation === 'Created' && change.outputOwner?.$kind === 'Shared',
);
if (!allowance) throw new Error('No allowance was created');

const allowanceId = allowance.objectId;
await client.core.waitForTransaction({ digest: result.Transaction.digest });
// Give allowanceId to the spender.

allowance::new takes Balance<USDC> as its type argument; the spending helpers take USDC. Creation also sends the funder an AllowanceCap, which can revoke the allowance.

Creating an allowance does not reserve or deposit funds. The funder must have enough USDC in its address balance when the spender withdraws.

Limits and expiration

An allowance requires both a lifetime cap and an expiration, unless it has a recurring rate limit. Lifetime caps must be positive u256 amounts. Start and expiration times use Unix milliseconds; if both are set, the start must be earlier. Names can be at most 128 bytes.

For recurring spending, use allowance::periodic_rate_limit(period_ms, limit) or allowance::monthly_rate_limit(limit). Wrap the result in 0x1::option::some<RateLimit> and pass it as the last argument to allowance::new. A rate limit can replace the lifetime cap and expiration, or apply alongside them.

Spending from an allowance

Pass the shared allowance ID and sign with the spender's key:

import { Transaction } from '@mysten/sui/transactions';

const USDC = '0xUsdcPackage::usdc::USDC';
const tx = new Transaction();
const balance = tx.balance({
	allowance: '0xAllowanceId',
	balance: 10_000_000n, // 10 USDC
	type: USDC,
});

tx.moveCall({
	target: '0xPackage::pool::deposit',
	typeArguments: [USDC],
	arguments: [tx.object('0xPoolId'), balance],
});

await client.core.signAndExecuteTransaction({ transaction: tx, signer: spender });

Use tx.coin() instead when the next operation expects a Coin<T>. Both helpers default to SUI, so pass type: USDC explicitly. Each amount must fit in a u64.

The SDK looks up the funder from the allowance ID. Spending draws only from that funder's address balance and must satisfy the allowance's limits and expiration. The sender or a sponsor must pay gas separately; do not combine allowance with useGasCoin.

Using a known funder

Pass the funder alongside the ID to skip the allowance lookup:

import { Transaction } from '@mysten/sui/transactions';

const USDC = '0xUsdcPackage::usdc::USDC';
const tx = new Transaction();
const coin = tx.coin({
	allowance: { objectId: '0xAllowanceId', funder: '0xFunderAddress' },
	balance: 10_000_000n, // 10 USDC
	type: USDC,
});
tx.transferObjects([coin], '0xRecipientAddress');

This skips the SDK's coin type and app binding checks; incorrect references fail on-chain. Building may still need a client to resolve object versions and gas.

App-bound allowances

App-bound allowances require a SpendPermit<A> from the app as well as the spender's signature. Pass the permit and app type to tx.balance() or tx.coin():

import { Transaction } from '@mysten/sui/transactions';

const USDC = '0xUsdcPackage::usdc::USDC';
const tx = new Transaction();
const permit = tx.moveCall({
	target: '0xAppPackage::subscriptions::authorize_payment',
	arguments: [tx.object('0xSubscriptionId')],
});
const balance = tx.balance({
	balance: 10_000_000n, // 10 USDC
	type: USDC,
	allowance: {
		objectId: '0xAllowanceId',
		app: { type: '0xAppPackage::subscriptions::APP', permit },
	},
});
// Pass balance to the app's next operation.

To create an app-bound allowance, the funder calls allowance::propose_for_app. The app accepts the proposal through allowance::issue with a SettingsPermit<A>.

The authorization function and its arguments are defined by the app. The SDK handles the framework's app_balance_spend call. Add funder to the allowance reference to skip the metadata lookup.

A permit authorizes one spend but does not bind its amount or recipient. Apps that need to enforce those must redeem the withdrawal inside their own Move function, using tx.withdrawal() as the input.

import { Transaction } from '@mysten/sui/transactions';

const tx = new Transaction();
const withdrawal = tx.withdrawal({
	amount: 10_000_000n,
	type: '0xUsdcPackage::usdc::USDC',
	from: 'allowance',
	allowance: '0xAllowanceId',
	funder: '0xFunderAddress',
});
// Pass withdrawal to the app's Move function that redeems it.

from: 'allowance' requires both allowance and funder; this helper does not look them up.

On this page