Merge pull request #25 from SwenSchaeferjohann/airdrop-lite

feat: zk compression airdrop tool
This commit is contained in:
ARYAN
2024-12-20 18:51:21 +05:30
committed by GitHub
24 changed files with 944 additions and 207 deletions

View File

@@ -44,9 +44,9 @@ import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
// Initialize with private key and optional RPC URL
const agent = new SolanaAgentKit(
"your-wallet-private-key-as-base58",
"https://api.mainnet-beta.solana.com",
"your-openai-api-key"
"your-wallet-private-key-as-base58",
"https://api.mainnet-beta.solana.com",
"your-openai-api-key"
);
// Create LangChain tools
@@ -61,9 +61,9 @@ const tools = createSolanaTools(agent);
import { deploy_token } from "solana-agent-kit";
const result = await deploy_token(
agent,
9, // decimals
1000000 // initial supply
agent,
9, // decimals
1000000 // initial supply
);
console.log("Token Mint Address:", result.mint.toString());
@@ -75,15 +75,15 @@ console.log("Token Mint Address:", result.mint.toString());
import { deploy_collection } from "solana-agent-kit";
const collection = await deploy_collection(agent, {
name: "My NFT Collection",
uri: "https://arweave.net/metadata.json",
royaltyBasisPoints: 500, // 5%
creators: [
{
address: "creator-wallet-address",
percentage: 100,
},
],
name: "My NFT Collection",
uri: "https://arweave.net/metadata.json",
royaltyBasisPoints: 500, // 5%
creators: [
{
address: "creator-wallet-address",
percentage: 100,
},
],
});
```
@@ -94,11 +94,11 @@ import { trade } from "solana-agent-kit";
import { PublicKey } from "@solana/web3.js";
const signature = await trade(
agent,
new PublicKey("target-token-mint"),
100, // amount
new PublicKey("source-token-mint"),
300 // 3% slippage
agent,
new PublicKey("target-token-mint"),
100, // amount
new PublicKey("source-token-mint"),
300 // 3% slippage
);
```
@@ -109,8 +109,8 @@ import { lendAsset } from "solana-agent-kit";
import { PublicKey } from "@solana/web3.js";
const signature = await lendAsset(
agent,
100 // amount
agent,
100 // amount
);
```
@@ -120,8 +120,8 @@ const signature = await lendAsset(
import { stakeWithJup } from "solana-agent-kit";
const signature = await stakeWithJup(
agent,
1 // amount in SOL
agent,
1 // amount in SOL
);
```
@@ -131,13 +131,44 @@ const signature = await stakeWithJup(
import { fetchPrice } from "solana-agent-kit";
const price = await fetchPrice(
agent,
"JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" // Token mint address
agent,
"JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" // Token mint address
);
console.log("Price in USDC:", price);
```
### Send an SPL Token Airdrop via ZK Compression
```typescript
import {
sendCompressedAirdrop,
getAirdropCostEstimate,
} from "solana-agent-kit";
import { PublicKey } from "@solana/web3.js";
(async () => {
console.log(
"~Airdrop cost estimate:",
getAirdropCostEstimate(
1000, // recipients
30_000 // priority fee in lamports
)
);
const signature = await sendCompressedAirdrop(
agent,
new PublicKey("JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"), // mint
42, // amount per recipient
[
new PublicKey("1nc1nerator11111111111111111111111111111111"),
// ... add more recipients
],
30_000 // priority fee in lamports
);
})();
```
## API Reference
### Core Functions
@@ -174,6 +205,10 @@ Lend idle assets to earn interest with Lulo.
Stake SOL with Jupiter to earn rewards.
#### `sendCompressedAirdrop(agent, mintAddress, amount, recipients, priorityFeeInLamports?, shouldLog?)`
Send an SPL token airdrop to many recipients at low cost via ZK Compression.
## Dependencies
The toolkit relies on several key Solana and Metaplex libraries:
@@ -183,6 +218,8 @@ The toolkit relies on several key Solana and Metaplex libraries:
- @metaplex-foundation/mpl-token-metadata
- @metaplex-foundation/mpl-core
- @metaplex-foundation/umi
- @lightprotocol/compressed-token
- @lightprotocol/stateless.js
## Contributing

View File

@@ -1 +0,0 @@
solanaagentkit.xyz

View File

@@ -1 +1 @@
window.searchData = "data:application/octet-stream;base64,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";
window.searchData = "data:application/octet-stream;base64,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";

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -19,6 +19,8 @@
"@langchain/groq": "^0.1.2",
"@langchain/langgraph": "^0.2.27",
"@langchain/openai": "^0.3.13",
"@lightprotocol/compressed-token": "^0.17.1",
"@lightprotocol/stateless.js": "^0.17.1",
"@metaplex-foundation/mpl-core": "^1.1.1",
"@metaplex-foundation/mpl-token-metadata": "^3.3.0",
"@metaplex-foundation/umi": "^0.9.2",
@@ -35,6 +37,7 @@
},
"devDependencies": {
"@types/node": "^22.9.0",
"ts-node": "^10.9.2"
"ts-node": "^10.9.2",
"typescript": "^5.7.2"
}
}

581
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@ import {
getTokenDataByAddress,
getTokenDataByTicker,
stakeWithJup,
sendCompressedAirdrop,
} from "../tools";
import { CollectionOptions, PumpFunTokenOptions } from "../types";
import { DEFAULT_OPTIONS } from "../constants";
@@ -57,7 +58,7 @@ export class SolanaAgentKit {
uri: string,
symbol: string,
decimals: number = DEFAULT_OPTIONS.TOKEN_DECIMALS,
initialSupply?: number,
initialSupply?: number
) {
return deploy_token(this, name, uri, symbol, decimals, initialSupply);
}
@@ -87,11 +88,11 @@ export class SolanaAgentKit {
}
async resolveSolDomain(domain: string) {
return resolveSolDomain(this, domain)
return resolveSolDomain(this, domain);
}
async getPrimaryDomain(account: PublicKey) {
return getPrimaryDomain(this, account)
return getPrimaryDomain(this, account);
}
async trade(
@@ -136,9 +137,26 @@ export class SolanaAgentKit {
);
}
async stake(
amount: number,
) {
async stake(amount: number) {
return stakeWithJup(this, amount);
}
async sendCompressedAirdrop(
mintAddress: string,
amount: number,
decimals: number,
recipients: string[],
priorityFeeInLamports: number,
shouldLog: boolean
): Promise<string[]> {
return await sendCompressedAirdrop(
this,
new PublicKey(mintAddress),
amount,
decimals,
recipients.map((recipient) => new PublicKey(recipient)),
priorityFeeInLamports,
shouldLog
);
}
}

View File

@@ -181,7 +181,6 @@ export class SolanaMintNFTTool extends Tool {
try {
const parsedInput = JSON.parse(input);
const result = await this.solanaKit.mintNFT(
new PublicKey(parsedInput.collectionMint),
{
@@ -703,6 +702,50 @@ export class SolanaTokenDataByTickerTool extends Tool {
}
}
export class SolanaCompressedAirdropTool extends Tool {
name = "solana_compressed_airdrop";
description = `Airdrop SPL tokens with ZK Compression (also called as airdropping tokens)
Inputs (input is a JSON string):
mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required)
amount: number, the amount of tokens to airdrop per recipient, e.g., 42 (required)
decimals: number, the decimals of the token, e.g., 6 (required)
recipients: string[], the recipient addresses, e.g., ["1nc1nerator11111111111111111111111111111111"] (required)
priorityFeeInLamports: number, the priority fee in lamports. Default is 30_000. (optional)
shouldLog: boolean, whether to log progress to stdout. Default is false. (optional)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const txs = await this.solanaKit.sendCompressedAirdrop(
parsedInput.mintAddress,
parsedInput.amount,
parsedInput.decimals,
parsedInput.recipients,
parsedInput.priorityFeeInLamports || 30_000,
parsedInput.shouldLog || false
);
return JSON.stringify({
status: "success",
message: `Airdropped ${parsedInput.amount} tokens to ${parsedInput.recipients.length} recipients.`,
transactionHashes: txs,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
new SolanaBalanceTool(solanaKit),
@@ -724,5 +767,6 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaGetDomainTool(solanaKit),
new SolanaTokenDataTool(solanaKit),
new SolanaTokenDataByTickerTool(solanaKit),
new SolanaCompressedAirdropTool(solanaKit),
];
}

View File

@@ -57,16 +57,10 @@ export async function deploy_token(
builder.sendAndConfirm(umi, { confirm: { commitment: 'finalized' } });
console.log(
"Token deployed successfully. Mint address: ",
mint.publicKey.toString()
);
return {
mint: toWeb3JsPublicKey(mint.publicKey),
};
} catch (error: any) {
console.log(error);
throw new Error(`Token deployment failed: ${error.message}`);
}
}

View File

@@ -44,8 +44,6 @@ export async function getTokenAddressFromTicker(
.filter((pair: any) => pair.chainId === "solana")
.sort((a: any, b: any) => (b.fdv || 0) - (a.fdv || 0));
console.log("solanaPairs", solanaPairs);
solanaPairs = solanaPairs.filter(
(pair: any) =>
pair.baseToken.symbol.toLowerCase() === ticker.toLowerCase()

View File

@@ -12,5 +12,6 @@ export * from "./launch_pumpfun_token";
export * from "./lend";
export * from "./get_tps";
export * from "./get_token_data";
export * from './stake_with_jup';
export * from "./stake_with_jup";
export * from "./fetch_price";
export * from "./send_compressed_airdrop";

View File

@@ -0,0 +1,306 @@
import {
AddressLookupTableAccount,
ComputeBudgetProgram,
Connection,
Keypair,
PublicKey,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent/index.js";
import {
buildAndSignTx,
calculateComputeUnitPrice,
createRpc,
Rpc,
sendAndConfirmTx,
sleep,
} from "@lightprotocol/stateless.js";
import {
CompressedTokenProgram,
createTokenPool,
} from "@lightprotocol/compressed-token";
import { Account, getOrCreateAssociatedTokenAccount } from "@solana/spl-token";
// arbitrary
const MAX_AIRDROP_RECIPIENTS = 1000;
const MAX_CONCURRENT_TXS = 30;
/**
* Estimate the cost of an airdrop in lamports.
* @param numberOfRecipients Number of recipients
* @param priorityFeeInLamports Priority fee in lamports
* @returns Estimated cost in lamports
*/
export const getAirdropCostEstimate = (
numberOfRecipients: number,
priorityFeeInLamports: number
) => {
const baseFee = 5000;
const perRecipientCompressedStateFee = 300;
const txsNeeded = Math.ceil(numberOfRecipients / 15);
const totalPriorityFees = txsNeeded * (baseFee + priorityFeeInLamports);
return (
perRecipientCompressedStateFee * numberOfRecipients + totalPriorityFees
);
};
/**
* Send airdrop with ZK Compressed Tokens.
* @param agent Agent
* @param mintAddress SPL Mint address
* @param amount Amount to send per recipient
* @param decimals Decimals of the token
* @param recipients Recipient wallet addresses (no ATAs)
* @param priorityFeeInLamports Priority fee in lamports
* @param shouldLog Whether to log progress to stdout. Defaults to false.
*/
export async function sendCompressedAirdrop(
agent: SolanaAgentKit,
mintAddress: PublicKey,
amount: number,
decimals: number,
recipients: PublicKey[],
priorityFeeInLamports: number,
shouldLog: boolean = false
): Promise<string[]> {
if (recipients.length > MAX_AIRDROP_RECIPIENTS) {
throw new Error(
`Max airdrop can be ${MAX_AIRDROP_RECIPIENTS} recipients at a time. For more scale, use open source ZK Compression airdrop tools such as https://github.com/helius-labs/airship.`
);
}
const url = agent.connection.rpcEndpoint;
if (url.includes("devnet")) {
throw new Error("Devnet is not supported for airdrop. Please use mainnet.");
}
if (!url.includes("helius")) {
console.warn(
"Warning: Must use RPC with ZK Compression support. Double check with your RPC provider if in doubt."
);
}
let sourceTokenAccount: Account;
try {
sourceTokenAccount = await getOrCreateAssociatedTokenAccount(
agent.connection,
agent.wallet,
mintAddress,
agent.wallet.publicKey
);
} catch (error) {
throw new Error(
"Source token account not found and failed to create it. Please add funds to your wallet and try again."
);
}
try {
await createTokenPool(
agent.connection as unknown as Rpc,
agent.wallet,
mintAddress
);
} catch (error: any) {
if (error.message.includes("already in use")) {
// skip
} else {
throw error;
}
}
return await processAll(
agent,
amount * 10 ** decimals,
mintAddress,
recipients,
priorityFeeInLamports,
shouldLog
);
}
async function processAll(
agent: SolanaAgentKit,
amount: number,
mint: PublicKey,
recipients: PublicKey[],
priorityFeeInLamports: number,
shouldLog: boolean
): Promise<string[]> {
const mintAddress = mint;
const payer = agent.wallet;
const sourceTokenAccount = await getOrCreateAssociatedTokenAccount(
agent.connection,
agent.wallet,
mintAddress,
agent.wallet.publicKey
);
const maxRecipientsPerInstruction = 5;
const maxIxs = 3; // empirically determined (as of 12/15/2024)
const lookupTableAddress = new PublicKey(
"9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ"
);
const lookupTableAccount = (
await agent.connection.getAddressLookupTable(lookupTableAddress)
).value!;
const batches: PublicKey[][] = [];
for (
let i = 0;
i < recipients.length;
i += maxRecipientsPerInstruction * maxIxs
) {
batches.push(recipients.slice(i, i + maxRecipientsPerInstruction * maxIxs));
}
const instructionSets = await Promise.all(
batches.map(async (recipientBatch) => {
const instructions: TransactionInstruction[] = [
ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }),
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: calculateComputeUnitPrice(
priorityFeeInLamports,
500_000
),
}),
];
const compressIxPromises = [];
for (
let i = 0;
i < recipientBatch.length;
i += maxRecipientsPerInstruction
) {
const batch = recipientBatch.slice(i, i + maxRecipientsPerInstruction);
compressIxPromises.push(
CompressedTokenProgram.compress({
payer: payer.publicKey,
owner: payer.publicKey,
source: sourceTokenAccount.address,
toAddress: batch,
amount: batch.map(() => amount),
mint: mintAddress,
})
);
}
const compressIxs = await Promise.all(compressIxPromises);
return [...instructions, ...compressIxs];
})
);
const url = agent.connection.rpcEndpoint;
const rpc = createRpc(url, url, url);
const results = [];
let confirmedCount = 0;
const totalBatches = instructionSets.length;
const renderProgressBar = (current: number, total: number) => {
const percentage = Math.floor((current / total) * 100);
const filled = Math.floor((percentage / 100) * 20);
const empty = 20 - filled;
const bar = "█".repeat(filled) + "░".repeat(empty);
return `Airdropped to ${Math.min(current * 15, recipients.length)}/${
recipients.length
} recipients [${bar}] ${percentage}%`;
};
const log = (message: string) => {
if (shouldLog && typeof process !== "undefined" && process.stdout) {
process.stdout.write(message);
}
};
for (let i = 0; i < instructionSets.length; i += MAX_CONCURRENT_TXS) {
const batchPromises = instructionSets
.slice(i, i + MAX_CONCURRENT_TXS)
.map((instructions, idx) =>
sendTransactionWithRetry(
rpc,
instructions,
payer,
lookupTableAccount,
i + idx
).then((signature) => {
confirmedCount++;
log("\r" + renderProgressBar(confirmedCount, totalBatches));
return signature;
})
);
const batchResults = await Promise.allSettled(batchPromises);
results.push(...batchResults);
}
log("\n");
const failures = results
.filter((r) => r.status === "rejected")
.map((r, idx) => ({
index: idx,
error: (r as PromiseRejectedResult).reason,
}));
if (failures.length > 0) {
throw new Error(
`Failed to process ${failures.length} batches: ${failures
.map((f) => f.error)
.join(", ")}`
);
}
return results.map((r) => (r as PromiseFulfilledResult<string>).value);
}
async function sendTransactionWithRetry(
connection: Rpc,
instructions: TransactionInstruction[],
payer: Keypair,
lookupTableAccount: AddressLookupTableAccount,
batchIndex: number
): Promise<string> {
const MAX_RETRIES = 3;
const INITIAL_BACKOFF = 500; // ms
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
const { blockhash } = await connection.getLatestBlockhash();
const tx = buildAndSignTx(
instructions,
payer,
blockhash,
[],
[lookupTableAccount]
);
const signature = await sendAndConfirmTx(connection, tx);
return signature;
} catch (error: any) {
const isRetryable =
error.message?.includes("blockhash not found") ||
error.message?.includes("timeout") ||
error.message?.includes("rate limit") ||
error.message?.includes("too many requests");
if (!isRetryable || attempt === MAX_RETRIES - 1) {
throw new Error(
`Batch ${batchIndex} failed after ${attempt + 1} attempts: ${
error.message
}`
);
}
const backoff =
INITIAL_BACKOFF * Math.pow(2, attempt) * (0.5 + Math.random());
await sleep(backoff);
}
}
throw new Error("Unreachable");
}

View File

@@ -41,7 +41,7 @@ export async function getPriorityFees(connection: Connection): Promise<{
const median =
sortedFees.length % 2 === 0
? ((sortedFees[mid - 1] ?? 0) + (sortedFees[mid] ?? 0)) / 2
: (sortedFees[mid] ?? 0);
: sortedFees[mid] ?? 0;
// Helper to create priority fee IX based on chosen strategy
const createPriorityFeeIx = (fee: number) => {
@@ -76,7 +76,7 @@ export async function getPriorityFees(connection: Connection): Promise<{
export async function sendTx(
agent: SolanaAgentKit,
tx: Transaction,
otherKeypairs?: Keypair[],
otherKeypairs?: Keypair[]
) {
tx.recentBlockhash = (await agent.connection.getLatestBlockhash()).blockhash;
tx.feePayer = agent.wallet_address;
@@ -90,8 +90,9 @@ export async function sendTx(
await agent.connection.confirmTransaction({
signature: txid,
blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
lastValidBlockHeight: (await agent.connection.getLatestBlockhash())
.lastValidBlockHeight,
lastValidBlockHeight: (
await agent.connection.getLatestBlockhash()
).lastValidBlockHeight,
});
return txid;
}