Skip to content

feat(js): add token transfer destination assertion - #190

Open
amilz wants to merge 1 commit into
solana-program:mainfrom
amilz:feat/DEV-1241-token-transfer-destination
Open

amilz wants to merge 1 commit into
solana-program:mainfrom
amilz:feat/DEV-1241-token-transfer-destination

Conversation

@amilz

@amilz amilz commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Follow-up to feat(js): add guarded SOL transfer helper system#96: adds assertValidTokenTransferDestination so exchanges can check a token withdrawal destination before building transferToATA. Same shape as assertValidTransferSolDestination.
  • Takes the same mint / destination / recipient inputs as getTransferToATAInstructionPlan, fetches the recipient and destination, and throws an InvalidTokenTransferDestinationError (typed destination, recipient, reason, plus owner? / mint? / tokenOwner?).
  • Follows the address verification guide. Off-curve recipients (multisigs, smart wallets) are allowed by default.
  • allowNonAssociatedTokenAccount: true accepts an existing token account for mint owned by recipient. Transfer to these with transferChecked, since transferToATA` only works with the ATA.
  • allowMissingAssociatedTokenAccount: false requires the ATA to already exist, for teams that won't pay its rent or want proof the recipient has held the mint.
reason When Override
unexpected-owner Recipient exists but isn't owned by the expected program (e.g. a token account passed as the recipient) programOwner
unfunded-recipient Recipient has no account on-chain (often a typo) allowUnfundedRecipient: true
off-curve Recipient is a PDA, only when allowOffCurve: false default (allowOffCurve: true)
ata-mismatch destination isn't the recipient's ATA for mint allowNonAssociatedTokenAccount: true
missing-token-account Destination doesn't exist (always for non-ATAs; for the ATA only when allowMissingAssociatedTokenAccount: false) default (allowMissingAssociatedTokenAccount: true)
not-token-account Non-ATA destination isn't an initialized token account —
mint-mismatch Non-ATA destination holds another mint —
token-owner-mismatch Destination isn't owned by the recipient (e.g. a reassigned ATA) —

Example

await assertValidTokenTransferDestination(rpc, { mint, destination, recipient }, { allowOffCurve: false });
await client.token.instructions.transferToATA({ mint, destination, recipient, authority, amount, decimals });

Closes DEV-1241

@joncinque
joncinque requested a review from mcintyre94 October 20, 2026 15:49

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant