How to approve and revoke delegate authority
Complete guide to approve with `approve()` and revoke delegate authority with `revoke()` for compressed tokens, troubleshooting and advanced configurations.
The approve()
and revoke()
functions grant and remove delegate spending authority for compressed tokens. Only the token owner can perform these operations.
Before we approve or revoke delegates, we need:
Compressed token accounts to delegate or revoke delegation from
SPL mint registered with the compressed token program via
createMint()
orcreateTokenPool()
The functions perform opposite operations:
approve()
consumes input account and creates delegated account with spending limit for delegate and change account for ownerrevoke()
consumes delegated accounts and creates output account under owner control without delegation
import { approve, revoke } from '@lightprotocol/compressed-token';
import { Keypair, PublicKey } from '@solana/web3.js';
const mint = new PublicKey("YOUR_EXISTING_MINT_ADDRESS");
const delegate = Keypair.generate();
const amount = 500_000_000;
const owner = payer;
// Approve delegate
const approveSignature = await approve(
rpc,
payer,
mint, // SPL mint with token pool for compression
amount,
owner,
delegate.publicKey, // delegate account
);
// Get delegated accounts for revocation
const delegatedAccounts = await rpc.getCompressedTokenAccountsByDelegate(
delegate.publicKey,
{ mint }
);
// Revoke delegate authority
const revokeSignature = await revoke(
rpc,
payer,
delegatedAccounts.items, // delegated compressed token accounts
owner,
);
Full Code Example
Approving Delegates
Run this script to approve delegate authority for compressed tokens!
// 1. Setup funded payer and connect to local validator
// 2. Create mint and token pool with initial tokens
// 3. Call approve() with mint, amount, owner, delegate
// 4. Verify delegation via getCompressedTokenAccountsByDelegate
import { Keypair, PublicKey } from '@solana/web3.js';
import { createRpc } from '@lightprotocol/stateless.js';
import {
createMint,
mintTo,
approve
} from '@lightprotocol/compressed-token';
import BN from 'bn.js';
import * as fs from 'fs';
async function approveDelegates() {
// Step 1: Setup funded payer and connect to local validator
const rpc = createRpc(); // defaults to localhost:8899
const payer = Keypair.generate();
const airdropSignature = await rpc.requestAirdrop(payer.publicKey, 1000000000); // 1 SOL
await rpc.confirmTransaction(airdropSignature);
// Step 2: Create SPL mint with token pool and mint initial tokens
const { mint } = await createMint(
rpc,
payer,
payer.publicKey, // mint authority
9 // decimals
);
console.log("SPL mint with token pool created:", mint.toBase58());
const tokenOwner = Keypair.generate();
const initialAmount = 1_000_000_000; // 1 token with 9 decimals
await mintTo(
rpc,
payer,
mint, // SPL mint with token pool for compression
tokenOwner.publicKey, // recipient
payer, // mint authority
initialAmount
);
console.log("Initial tokens minted:", initialAmount / 1_000_000_000, "tokens");
console.log("Token owner:", tokenOwner.publicKey.toBase58());
// Generate delegate address and define amount to approve for delegation
const delegate = Keypair.generate();
const delegateAmount = 500_000_000; // 0.5 tokens
// Step 3: Call approve() with mint, amount, owner, delegate
const approveTx = await approve(
rpc,
payer,
mint, // SPL mint with token pool for compression
delegateAmount,
tokenOwner, // owner keypair
delegate.publicKey // delegate address
);
console.log("Delegate approved");
console.log("Delegate:", delegate.publicKey.toBase58());
console.log("Approved amount:", delegateAmount / 1_000_000_000, "tokens");
console.log("Transaction:", approveTx);
// Step 4: Verify delegation via getCompressedTokenAccountsByDelegate
const delegateAccounts = await rpc.getCompressedTokenAccountsByDelegate(
delegate.publicKey,
{ mint }
);
// Check delegated balance
if (delegateAccounts.items.length > 0) {
const delegatedBalance = delegateAccounts.items.reduce(
(sum, account) => sum.add(account.parsed.amount),
new BN(0)
);
console.log("Verified delegation:", delegatedBalance.toNumber() / 1_000_000_000, "tokens");
}
// Save state for revoke step
const approveState = {
mint: mint.toBase58(),
tokenOwner: Array.from(tokenOwner.secretKey),
delegate: Array.from(delegate.secretKey),
delegatePublicKey: delegate.publicKey.toBase58(),
payer: Array.from(payer.secretKey)
};
fs.writeFileSync('./approve-state.json', JSON.stringify(approveState, null, 2));
return {
mint,
tokenOwner,
delegate: delegate.publicKey,
approveTransaction: approveTx,
delegatedAmount: delegateAmount
};
}
approveDelegates().catch(console.error);
Revoking Delegates
Remove delegate authority from previously approved accounts.
// Continue from approve setup - revoke delegate authority
// 1. Get delegated accounts to revoke via getCompressedTokenAccountsByDelegate
// 2. Call revoke() with delegated accounts and token owner - remove delegate authority and return control to owner
// 3. Verify delegation removed via getCompressedTokenAccountsByDelegate
import { Keypair, PublicKey } from '@solana/web3.js';
import { createRpc } from '@lightprotocol/stateless.js';
import { revoke } from '@lightprotocol/compressed-token';
import BN from 'bn.js';
import * as fs from 'fs';
async function revokeDelegates() {
if (!fs.existsSync('./approve-state.json')) {
console.log("No delegated accounts found to revoke. Please run 'npx tsx approve.ts' first.");
return;
}
// Load approve state
const state = JSON.parse(fs.readFileSync('./approve-state.json', 'utf8'));
const rpc = createRpc(); // defaults to localhost:8899
const payer = Keypair.fromSecretKey(new Uint8Array(state.payer));
const mint = new PublicKey(state.mint);
const tokenOwner = Keypair.fromSecretKey(new Uint8Array(state.tokenOwner));
const delegate = new PublicKey(state.delegatePublicKey);
// Step 1: Get delegated accounts to revoke via getCompressedTokenAccountsByDelegate
const delegateAccounts = await rpc.getCompressedTokenAccountsByDelegate(
delegate,
{ mint }
);
const delegatedBalanceBefore = delegateAccounts.items.reduce(
(sum, account) => sum.add(account.parsed.amount),
new BN(0)
);
console.log("Delegated balance:", delegatedBalanceBefore.toNumber() / 1_000_000_000, "tokens");
console.log("Revoke Delegate");
// Step 2: Call revoke() with delegated accounts and token owner
// Remove delegate authority and return control to owner
const revokeTx = await revoke(
rpc,
payer,
delegateAccounts.items, // delegated accounts to revoke
tokenOwner, // owner of compressed tokens
);
console.log("Delegate revoked!");
console.log("Transaction:", revokeTx);
// Step 3: Verify delegation removed via getCompressedTokenAccountsByDelegate
const delegateAccountsAfter = await rpc.getCompressedTokenAccountsByDelegate(
delegate,
{ mint }
);
const ownerAccounts = await rpc.getCompressedTokenAccountsByOwner(
tokenOwner.publicKey,
{ mint }
);
const ownerBalance = ownerAccounts.items.reduce(
(sum, account) => sum.add(account.parsed.amount),
new BN(0)
);
console.log("\nFinal balances:");
console.log("Delegated accounts:", delegateAccountsAfter.items.length);
console.log("Owner balance after revocation:", ownerBalance.toNumber() / 1_000_000_000, "tokens");
return {
revokeTransaction: revokeTx,
finalOwnerBalance: ownerBalance
};
}
revokeDelegates().catch(console.error);
Success!
You've successfully approved and revoked delegation for compressed tokens. The output shows:
Approval completion: Delegate authority granted for specified token amount
Delegation verification: Confirmed delegate accounts exist with correct amounts
Revocation completion: Delegate authority removed and tokens returned to owner
Troubleshooting
Advanced Configuration
Next Steps
That's it! Explore more guides in our cookbook section, or check out the advanced guides.
Last updated