Skip to content

Commit

Permalink
Add claim cli command to readme
Browse files Browse the repository at this point in the history
Adds description of how to claim tokens and removes reduntant claimant arg from cli
  • Loading branch information
ebatsell authored Dec 7, 2023
2 parents 5d27000 + 95cae7f commit 5d20c36
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 31 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# merkle-distributor

A program for distributing tokens efficiently via uploading a [Merkle root](https://en.wikipedia.org/wiki/Merkle_tree).

## Claiming Airdrop via CLI

To claim via CLI instead of using `https://jito.network/airdrop`, run the following commands.

1. Build the cli (must have rust + cargo installed):

```bash
cargo b -r
```

2. Run `claim` with the proper args. Be sure to replace `<YOUR KEYPAIR>` with the _full path_ of your keypair file. This will transfer tokens from the account `8Xm3tkQH581s3MoRHWUNYA5jKbgPATW4tJAAxgwDC6T6` to a the associated token account owned by your keypair, creating it if it doesn't exist.

```bash
./target/release/cli --rpc-url https://api.mainnet-beta.solana.com --keypair-path <YOUR KEYPAIR> --airdrop-version 0 --mint jtojtomepa8beP8AuQc6eXt5FriJwfFMwQx2v2f9mCL --program-id mERKcfxMC5SqJn4Ld4BUris3WKZZ1ojjWJ3A3J5CKxv claim --merkle-tree-path merkle-tree.json
```

Note that for searchers and validators, not all tokens will be vested until December 7, 2024. You can check the vesting status at `https://jito.network/airdrop`.
49 changes: 18 additions & 31 deletions cli/src/bin/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ pub enum Commands {
// NewClaim and Claim subcommand args
#[derive(Parser, Debug)]
pub struct ClaimArgs {
/// Claimant pubkey
#[clap(long, env)]
pub claimant: Pubkey,
/// Merkle distributor path
#[clap(long, env)]
pub merkle_tree_path: PathBuf,
Expand Down Expand Up @@ -143,6 +140,8 @@ fn main() {

fn process_new_claim(args: &Args, claim_args: &ClaimArgs) {
let keypair = read_keypair_file(&args.keypair_path).expect("Failed reading keypair file");
let claimant = keypair.pubkey();
println!("Claiming tokens for user {}...", claimant);

let merkle_tree = AirdropMerkleTree::new_from_file(&claim_args.merkle_tree_path)
.expect("failed to load merkle tree from file");
Expand All @@ -151,14 +150,13 @@ fn process_new_claim(args: &Args, claim_args: &ClaimArgs) {
get_merkle_distributor_pda(&args.program_id, &args.mint, args.airdrop_version);

// Get user's node in claim
let node = merkle_tree.get_node(&claim_args.claimant);
let node = merkle_tree.get_node(&claimant);

let (claim_status_pda, _bump) =
get_claim_status_pda(&args.program_id, &claim_args.claimant, &distributor);
let (claim_status_pda, _bump) = get_claim_status_pda(&args.program_id, &claimant, &distributor);

let client = RpcClient::new_with_commitment(&args.rpc_url, CommitmentConfig::confirmed());

let claimant_ata = get_associated_token_address(&claim_args.claimant, &args.mint);
let claimant_ata = get_associated_token_address(&claimant, &args.mint);

let mut ixs = vec![];

Expand All @@ -168,12 +166,8 @@ fn process_new_claim(args: &Args, claim_args: &ClaimArgs) {
// TODO: directly pattern match on error kind
if e.to_string().contains("AccountNotFound") {
println!("PDA does not exist. creating.");
let ix = create_associated_token_account(
&claim_args.claimant,
&claim_args.claimant,
&args.mint,
&token::ID,
);
let ix =
create_associated_token_account(&claimant, &claimant, &args.mint, &token::ID);
ixs.push(ix);
} else {
panic!("Error fetching PDA: {e}")
Expand All @@ -188,7 +182,7 @@ fn process_new_claim(args: &Args, claim_args: &ClaimArgs) {
claim_status: claim_status_pda,
from: get_associated_token_address(&distributor, &args.mint),
to: claimant_ata,
claimant: claim_args.claimant,
claimant,
token_program: token::ID,
system_program: solana_program::system_program::ID,
}
Expand All @@ -204,12 +198,8 @@ fn process_new_claim(args: &Args, claim_args: &ClaimArgs) {
ixs.push(new_claim_ix);

let blockhash = client.get_latest_blockhash().unwrap();
let tx = Transaction::new_signed_with_payer(
&ixs,
Some(&claim_args.claimant.key()),
&[&keypair],
blockhash,
);
let tx =
Transaction::new_signed_with_payer(&ixs, Some(&claimant.key()), &[&keypair], blockhash);

let signature = client
.send_and_confirm_transaction_with_spinner(&tx)
Expand All @@ -219,13 +209,13 @@ fn process_new_claim(args: &Args, claim_args: &ClaimArgs) {

fn process_claim(args: &Args, claim_args: &ClaimArgs) {
let keypair = read_keypair_file(&args.keypair_path).expect("Failed reading keypair file");
let claimant = keypair.pubkey();

let (distributor, bump) =
get_merkle_distributor_pda(&args.program_id, &args.mint, args.airdrop_version);
println!("distributor pubkey {}", distributor);

let (claim_status_pda, _bump) =
get_claim_status_pda(&args.program_id, &claim_args.claimant, &distributor);
let (claim_status_pda, _bump) = get_claim_status_pda(&args.program_id, &claimant, &distributor);
println!("claim pda: {claim_status_pda}, bump: {bump}");

let client = RpcClient::new_with_commitment(&args.rpc_url, CommitmentConfig::confirmed());
Expand All @@ -243,7 +233,7 @@ fn process_claim(args: &Args, claim_args: &ClaimArgs) {
}
}

let claimant_ata = get_associated_token_address(&claim_args.claimant, &args.mint);
let claimant_ata = get_associated_token_address(&claimant, &args.mint);

let claim_ix = Instruction {
program_id: args.program_id,
Expand All @@ -252,7 +242,7 @@ fn process_claim(args: &Args, claim_args: &ClaimArgs) {
claim_status: claim_status_pda,
from: get_associated_token_address(&distributor, &args.mint),
to: claimant_ata,
claimant: claim_args.claimant,
claimant,
token_program: token::ID,
}
.to_account_metas(None),
Expand All @@ -262,7 +252,7 @@ fn process_claim(args: &Args, claim_args: &ClaimArgs) {
let blockhash = client.get_latest_blockhash().unwrap();
let tx = Transaction::new_signed_with_payer(
&[claim_ix],
Some(&claim_args.claimant.key()),
Some(&claimant.key()),
&[&keypair],
blockhash,
);
Expand Down Expand Up @@ -310,7 +300,7 @@ fn check_distributor_onchain_matches(
}

fn process_new_distributor(args: &Args, new_distributor_args: &NewDistributorArgs) {
let client = RpcClient::new_with_commitment(&args.rpc_url, CommitmentConfig::confirmed());
let client = RpcClient::new_with_commitment(&args.rpc_url, CommitmentConfig::finalized());

let keypair = read_keypair_file(&args.keypair_path).expect("Failed reading keypair file");
let merkle_tree = AirdropMerkleTree::new_from_file(&new_distributor_args.merkle_tree_path)
Expand Down Expand Up @@ -371,13 +361,10 @@ fn process_new_distributor(args: &Args, new_distributor_args: &NewDistributorArg
// See comments on new_distributor instruction inside the program to ensure this transaction
// didn't get frontrun.
// If this fails, make sure to run it again.
match client.send_and_confirm_transaction_with_spinner_and_commitment(
&tx,
CommitmentConfig::confirmed(),
) {
match client.send_and_confirm_transaction_with_spinner(&tx) {
Ok(_) => {}
Err(e) => {
println!("Failed to create MerkleDistributor: {}", e);
println!("Failed to create MerkleDistributor: {:?}", e);

// double check someone didn't frontrun this transaction with a malicious merkle root
if let Some(account) = client
Expand Down

0 comments on commit 5d20c36

Please sign in to comment.