Skip to content

Commit

Permalink
Distribute spl tokens (solana-labs#13559)
Browse files Browse the repository at this point in the history
* Add helpers to covert between sdk types

* Add distribute-spl-tokens to args and arg-parsing

* Build spl-token transfer-checked instructions

* Check spl-token balances properly

* Add display handling to support spl-token

* Small refactor to allow failures in allocation iter

* Use Associated Token Account for spl-token distributions

* Add spl token support to balances command

* Update readme

* Add spl-token tests

* Rename spl-tokens file

* Move a couple more things out of commands

* Stop requiring lockup_date heading for non-stake distributions

* Use epsilon for allocation retention
  • Loading branch information
CriesofCarrots authored Nov 19, 2020
1 parent 1ffab5d commit 2ef4369
Show file tree
Hide file tree
Showing 12 changed files with 795 additions and 77 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions account-decoder/src/parse_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ pub fn spl_token_v2_0_native_mint() -> Pubkey {
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
}

// A helper function to convert a solana_sdk::pubkey::Pubkey to spl_sdk::pubkey::Pubkey
pub fn spl_token_v2_0_pubkey(pubkey: &Pubkey) -> SplTokenPubkey {
SplTokenPubkey::from_str(&pubkey.to_string()).unwrap()
}

// A helper function to convert a spl_sdk::pubkey::Pubkey to solana_sdk::pubkey::Pubkey
pub fn pubkey_from_spl_token_v2_0(pubkey: &SplTokenPubkey) -> Pubkey {
Pubkey::from_str(&pubkey.to_string()).unwrap()
}

pub fn parse_token(
data: &[u8],
mint_decimals: Option<u8>,
Expand Down
4 changes: 4 additions & 0 deletions tokens/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ indexmap = "1.5.1"
indicatif = "0.15.0"
pickledb = "0.4.1"
serde = { version = "1.0", features = ["derive"] }
solana-account-decoder = { path = "../account-decoder", version = "1.5.0" }
solana-clap-utils = { path = "../clap-utils", version = "1.5.0" }
solana-cli-config = { path = "../cli-config", version = "1.5.0" }
solana-client = { path = "../client", version = "1.5.0" }
Expand All @@ -27,10 +28,13 @@ solana-sdk = { path = "../sdk", version = "1.5.0" }
solana-stake-program = { path = "../programs/stake", version = "1.5.0" }
solana-transaction-status = { path = "../transaction-status", version = "1.5.0" }
solana-version = { path = "../version", version = "1.5.0" }
spl-associated-token-account-v1-0 = { package = "spl-associated-token-account", version = "=1.0.1" }
spl-token-v2-0 = { package = "spl-token", version = "=3.0.0", features = ["no-entrypoint"] }
tempfile = "3.1.0"
thiserror = "1.0"

[dev-dependencies]
bincode = "1.3.1"
solana-core = { path = "../core", version = "1.5.0" }
solana-logger = { path = "../logger", version = "1.5.0" }
solana-program-test = { path = "../program-test", version = "1.5.0" }
118 changes: 115 additions & 3 deletions tokens/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ solana-tokens distribute-tokens --from <KEYPAIR> --input-csv <RECIPIENTS_CSV> --
Example output:

```text
Recipient Expected Balance (◎)
Recipient Expected Balance
3ihfUy1n9gaqihM5bJCiTAGLgWc5zo3DqVUS6T736NLM 42
UKUcTXgbeTYh65RaVV5gSf6xBHevqHvAXMo3e8Q6np8k 43
```
Expand Down Expand Up @@ -77,7 +77,7 @@ recipient,amount,lockup_date
Example output:

```text
Recipient Expected Balance (◎)
Recipient Expected Balance
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv 10
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr 42
```
Expand All @@ -102,7 +102,7 @@ solana-tokens distribute-tokens --transfer-amount 10 --from <KEYPAIR> --input-cs
Example output:

```text
Recipient Expected Balance (◎)
Recipient Expected Balance
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv 10
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr 10
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT 10
Expand All @@ -125,3 +125,115 @@ recipient address. That SOL can be used to pay transaction fees on staking
operations such as delegating stake. The rest of the allocation is put in
a stake account. The new stake account address is output in the transaction
log.

## Distribute SPL tokens

Distributing SPL Tokens works very similarly to distributing SOL, but requires
the `--owner` parameter to sign transactions. Each recipient account must be an
system account that will own an Associated Token Account for the SPL Token mint.
The Associated Token Account will be created, and funded by the fee_payer, if it
does not already exist.

Send SPL tokens to the recipients in `<RECIPIENTS_CSV>`.

Example recipients.csv:

```text
recipient,amount
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT,75.4
C56nwrDVFpPrqwGYsTgQxv1ZraTh81H14PV4RHvZe36s,10
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr,42.1
7qQPmVAQxEQ5djPDCtiEUrxaPf8wKtLG1m6SB1brejJ1,20
```

You can check the status of the recipients before beginning a distribution. You
must include the SPL Token mint address:

```bash
solana-tokens spl-token-balances --mint <ADDRESS> --input-csv <RECIPIENTS_CSV>
```

Example output:

```text
Token: JDte736XZ1jGUtfAS32DLpBUWBR7WGSHy1hSZ36VRQ5V
Recipient Expected Balance Actual Balance Difference
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT 75.40 0.00 -75.40
C56nwrDVFpPrqwGYsTgQxv1ZraTh81H14PV4RHvZe36s 10.000 Associated token account not yet created
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr 42.10 0.00 -42.10
7qQPmVAQxEQ5djPDCtiEUrxaPf8wKtLG1m6SB1brejJ1 20.000 Associated token account not yet created
```

To run the distribution:

```bash
solana-tokens distribute-spl-tokens --from <ADDRESS> --owner <KEYPAIR> \
--input-csv <RECIPIENTS_CSV> --fee-payer <KEYPAIR>
```

Example output:

```text
Total in input_csv: 147.5 tokens
Distributed: 0 tokens
Undistributed: 147.5 tokens
Total: 147.5 tokens
Recipient Expected Balance
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT 75.400
C56nwrDVFpPrqwGYsTgQxv1ZraTh81H14PV4RHvZe36s 10.000
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr 42.100
7qQPmVAQxEQ5djPDCtiEUrxaPf8wKtLG1m6SB1brejJ1 20.000
```

### Calculate what tokens should be sent

As with SOL, you can List the differences between a list of expected
distributions and the record of what transactions have already been sent using
the `--dry-run` parameter, or `solana-tokens balances`.

Example updated recipients.csv:

```text
recipient,amount
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT,100
C56nwrDVFpPrqwGYsTgQxv1ZraTh81H14PV4RHvZe36s,100
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr,100
7qQPmVAQxEQ5djPDCtiEUrxaPf8wKtLG1m6SB1brejJ1,100
```

Using dry-run:

```bash
solana-tokens distribute-tokens --dry-run --input-csv <RECIPIENTS_CSV>
```

Example output:

```text
Total in input_csv: 400 tokens
Distributed: 147.5 tokens
Undistributed: 252.5 tokens
Total: 400 tokens
Recipient Expected Balance
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT 24.600
C56nwrDVFpPrqwGYsTgQxv1ZraTh81H14PV4RHvZe36s 90.000
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr 57.900
7qQPmVAQxEQ5djPDCtiEUrxaPf8wKtLG1m6SB1brejJ1 80.000
```

Or:

```bash
solana-tokens balances --mint <ADDRESS> --input-csv <RECIPIENTS_CSV>
```

Example output:

```text
Token: JDte736XZ1jGUtfAS32DLpBUWBR7WGSHy1hSZ36VRQ5V
Recipient Expected Balance Actual Balance Difference
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT 100.000 75.400 -24.600
C56nwrDVFpPrqwGYsTgQxv1ZraTh81H14PV4RHvZe36s 100.000 10.000 -90.000
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr 100.000 42.100 -57.900
7qQPmVAQxEQ5djPDCtiEUrxaPf8wKtLG1m6SB1brejJ1 100.000 20.000 -80.000
```
Loading

0 comments on commit 2ef4369

Please sign in to comment.