Skip to content

Commit

Permalink
Add claim info to readme
Browse files Browse the repository at this point in the history
  • Loading branch information
ebatsell committed Dec 7, 2023
1 parent 5d27000 commit 4eb5221
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 4eb5221

Please sign in to comment.