Add associated-token-account documentation
This commit is contained in:
parent
d624117afd
commit
c6689eab8c
|
@ -5,6 +5,7 @@ module.exports = {
|
|||
"introduction",
|
||||
"token",
|
||||
"token-swap",
|
||||
"associated-token-account",
|
||||
"memo",
|
||||
"shared-memory",
|
||||
],
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
title: Associated Token Account Program
|
||||
---
|
||||
|
||||
This program defines the convention and the provides the mechanism for mapping
|
||||
the user's wallet address to the associated token accounts they hold.
|
||||
|
||||
It also enables sender-funded token transfers.
|
||||
|
||||
See the [SPL Token](token.md) program for more information about tokens in
|
||||
general.
|
||||
|
||||
## Background
|
||||
|
||||
Solana's programming model and the definitions of the Solana terms used in this
|
||||
document are available at:
|
||||
|
||||
- https://docs.solana.com/apps
|
||||
- https://docs.solana.com/terminology
|
||||
|
||||
## Source
|
||||
|
||||
The Associated Token Account Program's source is available on
|
||||
[github](https://github.com/solana-labs/solana-program-library).
|
||||
|
||||
|
||||
## Interface
|
||||
The Associated Token Account Program is written in Rust and available on
|
||||
[crates.io](https://crates.io/crates/spl-associated-token-account) and
|
||||
[docs.rs](https://docs.rs/spl-associated-token-account).
|
||||
|
||||
|
||||
### Finding the Associated Token Account address
|
||||
The associated token account for a given wallet address is simply a
|
||||
program-derived account consisting of the wallet address itself and the token mint.
|
||||
|
||||
The [get_associated_token_address](https://github.com/solana-labs/solana-program-library/blob/associated-token-account-v1.0.0/associated-token-account/program/src/lib.rs#L35)
|
||||
Rust function may be used by clients to derive the wallet's associated token address.
|
||||
|
||||
|
||||
The associated account address can be derived in Javascript with:
|
||||
```js
|
||||
import {PublicKey, PublicKeyNonce} from '@solana/web3.js';
|
||||
|
||||
const SPL_TOKEN_PROGRAM_ID: PublicKey = new PublicKey(
|
||||
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
|
||||
);
|
||||
const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: PublicKey = new PublicKey(
|
||||
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',
|
||||
);
|
||||
|
||||
async function findAssociatedTokenAddress(
|
||||
walletAddress: Pubkey,
|
||||
tokenMintAddress: Pubkey
|
||||
): Promise<PublicKey> {
|
||||
return PublicKey.findProgramAddress(
|
||||
[
|
||||
walletAddress.toBuffer(),
|
||||
SPL_TOKEN_PROGRAM_ID.toBuffer(),
|
||||
tokenMintAddress.toBuffer(),
|
||||
],
|
||||
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
|
||||
)[0];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Creating an Associated Token Account
|
||||
|
||||
If the associated token account for a given wallet address does not yet exist,
|
||||
it may be created by *anybody* by issuing a transaction containing the
|
||||
instruction return by [create_associated_token_account](https://github.com/solana-labs/solana-program-library/blob/associated-token-account-v1.0.0/associated-token-account/program/src/lib.rs#L54).
|
||||
|
||||
Regardless of creator the new associated token account will be fully owned by
|
||||
the wallet, as if the wallet itself had created it.
|
|
@ -21,13 +21,9 @@ The Token Program's source is available on
|
|||
|
||||
## Interface
|
||||
|
||||
The on-chain Token Program is written in Rust and available on crates.io as
|
||||
[spl-token](https://docs.rs/spl-token). The program's [instruction interface
|
||||
documentation](https://docs.rs/spl-token/2.0.4/spl_token/instruction/enum.TokenInstruction.html)
|
||||
can also be found there.
|
||||
The Token Program is written in Rust and available on [crates.io](https://crates.io/crates/spl-token) and [docs.rs](https://docs.rs/spl-token).
|
||||
|
||||
Auto-generated C bindings are also available for the on-chain Token Program and
|
||||
available
|
||||
Auto-generated C bindings are also available
|
||||
[here](https://github.com/solana-labs/solana-program-library/blob/master/token/program/inc/token.h)
|
||||
|
||||
[JavaScript
|
||||
|
@ -35,6 +31,9 @@ bindings](https://github.com/solana-labs/solana-program-library/blob/master/toke
|
|||
are available that support loading the Token Program on to a chain and issue
|
||||
instructions.
|
||||
|
||||
See the [SPL Associated Token Account](associated-token-account.md) program for
|
||||
convention around wallet address to token account mapping and funding.
|
||||
|
||||
## Command-line Utility
|
||||
|
||||
The `spl-token` command-line utility can be used to experiment with SPL
|
||||
|
@ -71,9 +70,8 @@ solana config set --url https://devnet.solana.com
|
|||
|
||||
#### Default Keypair
|
||||
|
||||
See [Keypair conventions]
|
||||
(https://docs.solana.com/cli/conventions#keypair-conventions) for information on
|
||||
how to setup a keypair if you don't already have one.
|
||||
See [Keypair conventions](https://docs.solana.com/cli/conventions#keypair-conventions)
|
||||
for information on how to setup a keypair if you don't already have one.
|
||||
|
||||
Keypair File
|
||||
```
|
||||
|
@ -161,7 +159,7 @@ Unwrapping GJTxcnA5Sydy8YRhqvHxbQ5QNsPyRKvzguodQEaShJje
|
|||
Signature: f7opZ86ZHKGvkJBQsJ8Pk81v8F3v1VUfyd4kFs4CABmfTnSZK5BffETznUU3tEWvzibgKJASCf7TUpDmwGi8Rmh
|
||||
```
|
||||
|
||||
### Example: Transferring tokens to another user, with sender-funding
|
||||
### Example: Transferring tokens to another user
|
||||
First the receiver uses `spl-token create-account` to create their associated
|
||||
token account for the Token type. Then the receiver obtains their wallet
|
||||
address by running `solana address` and provides it to the sender.
|
||||
|
@ -228,9 +226,6 @@ Account Token
|
|||
CqAxDdBRnawzx9q4PYM3wrybLHBhDZ4P6BTV13WsRJYJ AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 50
|
||||
```
|
||||
|
||||
### Example: Transferring tokens with sender funding
|
||||
If the recipient a
|
||||
|
||||
### Example: Create a non-fungible token
|
||||
|
||||
Create the token type,
|
||||
|
@ -526,105 +521,60 @@ Although all SPL Token accounts do have their own address on-chain, there's no
|
|||
need to surface these additional addresses to the user.
|
||||
|
||||
There are two programs that are used by the wallet:
|
||||
* SPL Token program - generic program that is used by all SPL Tokens
|
||||
* SPL Associated Token Account program - this program defines the convention and the
|
||||
provides the mechanism for mapping the user's wallet address to the associated
|
||||
token accounts they hold.
|
||||
|
||||
### Associated Token Account
|
||||
The associated token account convention allows all tokens to have the same
|
||||
destination address, allowing the user share their main wallet address to
|
||||
receive any SPL token.
|
||||
|
||||
The following Rust function can be used to derive the address of a user's
|
||||
associated token account for a given SPL Token mint:
|
||||
```rust
|
||||
/// Finds the associated token address for a given wallet
|
||||
/// address and SPL Token mint
|
||||
pub fn get_associated_token_address(
|
||||
wallet_address: &Pubkey,
|
||||
spl_token_mint_address: &Pubkey,
|
||||
) -> Pubkey {
|
||||
Pubkey::find_program_address(
|
||||
&[
|
||||
&primary_account_address.to_bytes(),
|
||||
&spl_token::id().to_bytes(),
|
||||
&spl_token_mint_address.to_bytes(),
|
||||
],
|
||||
&spl_associated_token_account::id()
|
||||
).0
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Javascript equivalent:
|
||||
```
|
||||
import {PublicKey, PublicKeyNonce} from '@solana/web3.js';
|
||||
|
||||
async function findAssociatedTokenAddress(
|
||||
walletAddress: Pubkey,
|
||||
tokenMintAddress: Pubkey
|
||||
): Promise<PublicKey> {
|
||||
return PublicKey.findProgramAddress(
|
||||
[
|
||||
walletAddress.toBuffer(),
|
||||
TOKEN_PROGRAM_ID.toBuffer(),
|
||||
tokenMintAddress.toBuffer(),
|
||||
],
|
||||
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
|
||||
)[0];
|
||||
}
|
||||
```
|
||||
* SPL Token program: generic program that is used by all SPL Tokens
|
||||
* [SPL Associated Token Account](associated-token-account.md) program: defines
|
||||
the convention and provides the mechanism for mapping the user's wallet
|
||||
address to the associated token accounts they hold.
|
||||
|
||||
### How to fetch and display token holdings
|
||||
The [getTokenAccountsByOwner](https://docs.solana.com/apps/jsonrpc-api#gettokenaccountsbyowner)
|
||||
JSON RPC method can be used to fetch all token accounts for a wallet address.
|
||||
|
||||
For each token mint, the wallet could have be multiple token accounts: the
|
||||
For each token mint, the wallet could have multiple token accounts: the
|
||||
associated token account and/or other ancillary token accounts
|
||||
|
||||
By convention it is suggested that wallets roll up the balances from all token
|
||||
accounts of the same token mint into a single balance for the user to shield the
|
||||
user from this complexity. See the
|
||||
[Garbage Collecting Ancillary Token Accounts](#garbage-collecting-ancillary-token-accounts)
|
||||
section for suggestions on how the wallet should clean up ancillary token accounts on the user's
|
||||
user from this complexity.
|
||||
|
||||
See the [Garbage Collecting Ancillary Token Accounts](#garbage-collecting-ancillary-token-accounts)
|
||||
section for suggestions on how the wallet should clean up ancillary token accounts on the user's behalf.
|
||||
|
||||
### Associated Token Account
|
||||
Before the user can receive tokens their associated token account must be created
|
||||
Before the user can receive tokens, their associated token account must be created
|
||||
on-chain, requiring a small amount of SOL to mark the account as rent-exempt.
|
||||
|
||||
There's no restriction on who can create a user's associated token account. It
|
||||
could either be created by the wallet on behalf of the user or funded by a 3rd
|
||||
party through an airdrop campaign.
|
||||
|
||||
Ultimately the
|
||||
`spl_associated_token_account::create_associated_token_account()` instruction
|
||||
just needs to be executed by some party.
|
||||
The creation process is described [here](associated-token-account.md#creating-an-associated-token-account).
|
||||
|
||||
#### Sample "Add Token" workflow
|
||||
When the user wants to receive tokens, they should fund their associated token
|
||||
account.
|
||||
The user should first fund their associated token when they want to receive tokens of a certain type.
|
||||
|
||||
To do so, the wallet should provide a UI that allow the users to "add a token".
|
||||
The wallet should provide a UI that allow the users to "add a token".
|
||||
The user selects the kind of token, and is presented with information about how
|
||||
much SOL it will cost to add the token. Upon confirmation, the wallet sends a
|
||||
transaction with the
|
||||
`spl_associated_token_account::create_associated_token_account()` instruction.
|
||||
much SOL it will cost to add the token.
|
||||
|
||||
Upon confirmation, the wallet creates the associated token type as the described
|
||||
[here](associated-token-account.md#creating-an-associated-token-account).
|
||||
|
||||
#### Sample "Airdrop campaign" workflow
|
||||
For each recipient wallet addresses, send a transaction containing:
|
||||
1. `spl_associated_token_account::create_associated_token_account()` to create
|
||||
the recipient's associated token account if necessary
|
||||
2. `TokenInstruction::Transfer` to complete the airdrop
|
||||
1. Create the associated token account on the recipient's behalf.
|
||||
2. Use `TokenInstruction::Transfer` to complete the transfer
|
||||
|
||||
#### Associated Token Account Ownership
|
||||
The wallet should never use `TokenInstruction::SetAuthority` to set the
|
||||
⚠️ The wallet should never use `TokenInstruction::SetAuthority` to set the
|
||||
`AccountOwner` authority of the associated token account to another address.
|
||||
|
||||
### Ancillary Token Accounts
|
||||
At any time ownership of an existing SPL Token account may be assigned to the
|
||||
user. One way to accomplish this is with the
|
||||
`spl-token authorize <TOKEN_ADDRESS> owner <USER_ADDRESS>` command.
|
||||
`spl-token authorize <TOKEN_ADDRESS> owner <USER_ADDRESS>` command. Wallets
|
||||
should be prepared to gracefully manage token accounts that they themselves did
|
||||
not create for the user.
|
||||
|
||||
### Transferring Tokens Between Wallets
|
||||
The preferred method of transferring tokens between wallets is to transfer into
|
||||
|
@ -632,9 +582,9 @@ associated token account of the recipient.
|
|||
|
||||
The recipient must provide their main wallet address to the sender. The sender
|
||||
then:
|
||||
1. Derives the associated token account for the recipient using `spl_associated_token_account::get_associated_token_address`
|
||||
1. Derives the associated token account for the recipient
|
||||
1. Fetches the recipient's associated token account over RPC and checks that it exists.
|
||||
1. If the recipient's associated token accountdoes not exist, the sender wallet may choose to first fund the recipient's wallet at their expense
|
||||
1. If the recipient's associated token account does not exist, the sender wallet may choose to first fund the recipient's wallet at their expense
|
||||
1. Use `TokenInstruction::Transfer` to complete the transfer.
|
||||
|
||||
### Registry for token details
|
||||
|
@ -663,3 +613,5 @@ Cleanup Pseudo Steps:
|
|||
If adding one or more of clean up instructions cause the transaction to exceed
|
||||
the maximum allowed transaction size, remove those extra clean up instructions.
|
||||
They can be cleaned up during the next send operation.
|
||||
|
||||
The `spl-token gc` command provides an example implementation of this cleanup process.
|
||||
|
|
Loading…
Reference in New Issue