> For the complete documentation index, see [llms.txt](/llms.txt)

# Allowances

Create allowances and spend from another account's address balance



An [allowance](https://docs.sui.io/references/framework/sui_sui/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 [#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.

```tsx
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](https://docs.sui.io/onchain-finance/asset-custody/address-balances/using-address-balances#sending-funds)
when the spender withdraws.

### Limits and expiration [#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 [#spending-from-an-allowance]

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

```tsx
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 [#using-a-known-funder]

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

```tsx
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]

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()`:

```tsx
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.

```tsx
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.
