> ## Documentation Index
> Fetch the complete documentation index at: https://sdk-guide.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Token approvals

> List the token allowances of your wallets and revoke or reduce them on EVM, Solana and Tron

An approval (allowance) lets a spender contract move tokens out of a wallet. The SDK lists the
approvals of your wallets with risk flags and can lower or remove them on EVM (ERC-20), Tron
(TRC-20) and Solana (SPL delegates). It never grants or raises one.

| Method | API key permission |
| :- | :- |
| `listAllowances` | View balances |
| `refreshAllowances`, `recheckAllowance`, `planAllowanceChange`, `revokeAllowance`, `reduceAllowance`, `prepareHardwareAllowanceChange` | Transaction management |

## Listing

```typescript theme={null}
const items = await sdk.listAllowances();                    // personal wallets
const { organizationId } = await sdk.getOrgMe();
const orgItems = await sdk.listAllowances({ organizationId }); // one organization's wallets
```

Without `organizationId` the list is personal only. `getOrgMe()` returns the id of the caller's
organization; pass it to list, refresh and recheck that organization's wallets.

Each `AllowanceItem` carries:

| Field | Meaning |
| :- | :- |
| `organizationId` | The organization the wallet belongs to, `null` for a personal wallet |
| `chainType`, `chainId`, `chainName` | `EVM`, `SOL` or `TRON`, and the network |
| `wallet` | `{ id, address, name }` |
| `token` | `{ address, symbol, name, decimals, logoUrl }`. `decimals` can be `null` |
| `spender` | `{ address, label, protocol, category, isKnown, isContract }` |
| `allowanceRaw` | The allowance in the token's smallest unit, digits only |
| `allowanceFormatted` | A display value, when the decimals are known |
| `isUnlimited` | The allowance is the maximum value |
| `riskLevel`, `riskReasons` | `high`, `medium` or `low`, and why: `FLAGGED_SPENDER`, `EOA_SPENDER`, `UNKNOWN_SPENDER`, `UNLIMITED` |

The list comes from the server's index, not from the chain. To scan again:

```typescript theme={null}
const { queued, disabled, nextAllowedAt } = await sdk.refreshAllowances({ organizationId });
```

Refresh is rate limited. When the answer carries `nextAllowedAt`, wait until then. `disabled: true`
means scanning is turned off for the account.

`recheckAllowance(request)` reads one approval back from the chain through the server. The change
methods call it for you.

## Revoking and reducing

```typescript theme={null}
await sdk.runWithUserRef(userRef, async () => {
  await sdk.revokeAllowance({ allowance, userRef, walletPassword });
  await sdk.reduceAllowance({ allowance, amountAtomic: '250000', userRef, walletPassword });
});
// => { txHashes: string[], recheck: 'updated' | 'pending', confirmation, allowance? }
```

* **Revoke** sets the allowance to `0` and sends no native value.
* **Reduce** takes `amountAtomic`, a string of digits in the token's smallest unit. There is no
  decimal form, so convert with `token.decimals` yourself. The new amount must be below the current
  allowance and above `0`: use `revokeAllowance` for `0`.
* Pass the item exactly as you listed it. An organization item is changed through the organization
  routes automatically.
* The server accepts the change only if it lowers an allowance it can verify on chain. A refusal is
  thrown before anything is signed.
* `walletPassword` is needed in [strict mode](https://sdk-guide.mintlify.site/sdk-guide/strict-authentication.md), and for wallets
  derived from the recovery phrase.

### The result

By default the methods return right after the broadcast, with `recheck: 'pending'` and
`confirmation: 'unchecked'`. Read the new value later with `recheckAllowance` or `listAllowances`.

Pass `waitForConfirmation: true` to wait for the last transaction (up to 90 s on EVM, 60 s on
Solana, 180 s on Tron) and read the allowance back:

* `recheck: 'updated'`: confirmed and read back; `allowance` holds the new value.
* `recheck: 'pending'`: the transaction did not confirm in time, failed, or the read-back failed.
  `confirmation` says which (`confirmed`, `failed` or `timeout`). This never throws, because the
  transactions are already out.

## Tokens that need two transactions

Some tokens (USDT-style) refuse to move a non-zero allowance straight to another non-zero value.
For those, a reduce takes two transactions: set to `0`, then set to the new amount. The SDK finds
out by simulating against the node, never from a list. Solana is never affected, and a revoke never
is.

Check before the user confirms, and pass the plan on so it is not simulated twice:

```typescript theme={null}
const plan = await sdk.planAllowanceChange({ allowance, mode: 'reduce', amountAtomic });
if (plan.zeroFirst) {
  // tell the user: two transactions, the allowance is 0 in between
}
await sdk.reduceAllowance({ allowance, amountAtomic, plan, userRef, walletPassword });
```

Between the two steps the spender keeps the old allowance until the first one confirms, and the
allowance is `0` after it. On Tron the second transaction is sent only after the first is confirmed,
whatever `waitForConfirmation` says.

If the first transaction went out and the second did not, the SDK throws
`AllowanceChangePartialError` with `txHashes` holding what was sent. The allowance may now be `0`.
Do not retry blindly: read where it stands with `recheckAllowance` or `listAllowances`, then reduce
again from there.

## Hardware wallets

A wallet on a hardware root uses the [hardware session flow](https://sdk-guide.mintlify.site/sdk-guide/hardware-wallets.md):

```typescript theme={null}
const request = await sdk.prepareHardwareAllowanceChange({ allowance, mode: 'revoke' });
// run request.calls on the device, then submitHardwareStep
// final result: { action: 'allowance-change', txHash, recheck: 'pending', confirmation: 'unchecked' }
```

The device signs one transaction per action, so it can revoke, and reduce only when a single
transaction is enough. A reduce that needs two throws `HardwareAllowanceZeroFirstUnsupportedError`
before the device is asked: offer a revoke instead. No wallet password is involved.

`prepareHardwareAllowanceChange` covers personal wallets only. `revokeAllowance` and
`reduceAllowance` throw `HardwareSignerRequiredError` for a hardware wallet.

## Errors

| Error | When |
| :- | :- |
| `InvalidInputError` | `amountAtomic` is not digits, is `0` on a reduce, or the item belongs to another network than the SDK is configured for |
| `TransactionError` | The token refuses both the change and a change to zero (`This token refuses the change.`) |
| `AllowanceChangePartialError` | A two-transaction change stopped after the first one. Carries `txHashes` |
| `HardwareAllowanceZeroFirstUnsupportedError` | A hardware reduce that needs two transactions |
| `HardwareSignerRequiredError` | `revokeAllowance` or `reduceAllowance` was called for a hardware wallet |
| `StrictAuthPasswordRequiredError` | Strict mode is on and no `walletPassword` was passed |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.