Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Example .env file
RPC_ENDPOINT="https://devnet.helius-rpc.com?api-key=<YOUR_API_KEY>"
RPC_ENDPOINT="https://mainnet.helius-rpc.com?api-key=<YOUR_API_KEY>"
PAYER_KEYPAIR="bs58encoded string of secret key"
MINT_ADDRESS="D3tw8PvgsJqqFBUKcJxiLAe78wbvu3jzospbtbTt9u18"
AUTHORITY_KEYPAIR="bs58encoded string of secret key"
Expand Down
111 changes: 111 additions & 0 deletions src/scripts/compress-idempotent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as web3 from "@solana/web3.js";
import { Keypair, PublicKey } from "@solana/web3.js";
import {
CompressedTokenProgram,
getTokenPoolInfos,
selectTokenPoolInfo,
} from "@lightprotocol/compressed-token";
import {
bn,
buildAndSignTx,
calculateComputeUnitPrice,
createRpc,
dedupeSigner,
Rpc,
selectStateTreeInfo,
sendAndConfirmTx,
} from "@lightprotocol/stateless.js";
import * as splToken from "@solana/spl-token";
import dotenv from "dotenv";
import bs58 from "bs58";
import { createIdempotentAirdropInstruction } from "./idempotent";
dotenv.config();

const RPC_ENDPOINT = process.env.RPC_ENDPOINT;
const MINT_ADDRESS = new PublicKey(process.env.MINT_ADDRESS!);
const PAYER_KEYPAIR = Keypair.fromSecretKey(
bs58.decode(process.env.PAYER_KEYPAIR!)
);

(async () => {
try {
const connection: Rpc = createRpc(RPC_ENDPOINT);
const mintAddress = MINT_ADDRESS;
const payer = PAYER_KEYPAIR;

const amount = bn(111); // each recipient will receive 111 tokens
const recipients = [PAYER_KEYPAIR.publicKey].map(
(address) => new PublicKey(address)
);
const activeStateTrees = await connection.getStateTreeInfos();

/// Pick a new tree for each transaction!
const treeInfo = selectStateTreeInfo(activeStateTrees);

// Create an SPL token account for the sender.
// The sender will send tokens from this account to the recipients as compressed tokens.
const sourceTokenAccount = await splToken.getOrCreateAssociatedTokenAccount(
connection,
payer,
mintAddress,
payer.publicKey
);

// Airdrop to example recipients addresses
// 1 recipient = 120_000 CU
// 5 recipients = 170_000 CU
// with idempotent cPDA = +250_000 CU
const instructions: web3.TransactionInstruction[] = [];
instructions.push(
web3.ComputeBudgetProgram.setComputeUnitLimit({ units: 370_000 }),
web3.ComputeBudgetProgram.setComputeUnitPrice({
// Replace this with a dynamic priority_fee to land during high network load.
microLamports: calculateComputeUnitPrice(20_000, 370_000),
})
);

const compressInstruction = await CompressedTokenProgram.compress({
payer: payer.publicKey,
owner: payer.publicKey,
source: sourceTokenAccount.address,
toAddress: recipients,
amount: recipients.map(() => amount),
mint: mintAddress,
outputStateTreeInfo: treeInfo,
tokenPoolInfo: selectTokenPoolInfo(
await getTokenPoolInfos(connection, mintAddress)
),
});
instructions.push(compressInstruction);

// Creates a cPDA for a given set of recipients. This lets you retry txns without handling spends client-side.
// The whole txn will fail if the same set of seeds (with the same order) is used a second time.
instructions.push(
await createIdempotentAirdropInstruction(
connection,
payer.publicKey,
mintAddress,
recipients,
treeInfo
)
);

// Sign the transaction with the payer and owner keypair
const owner = payer;
const additionalSigners = dedupeSigner(payer, [owner]);

const { blockhash } = await connection.getLatestBlockhash();

const tx = buildAndSignTx(
instructions,
payer,
blockhash,
additionalSigners
);

const txId = await sendAndConfirmTx(connection, tx);
console.log(`txId: ${txId}`);
} catch (e) {
console.error(`Compression failed:`, e);
}
})();
35 changes: 5 additions & 30 deletions src/scripts/compress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
import * as splToken from "@solana/spl-token";
import dotenv from "dotenv";
import bs58 from "bs58";
import { createIdempotentAirdropInstruction } from "./idempotent";
dotenv.config();

const RPC_ENDPOINT = process.env.RPC_ENDPOINT;
Expand All @@ -33,11 +32,10 @@ const PAYER_KEYPAIR = Keypair.fromSecretKey(
const mintAddress = MINT_ADDRESS;
const payer = PAYER_KEYPAIR;

const amount = bn(333); // each recipient will receive 111 tokens
const recipients = [
"GMPWaPPrCeZPse5kwSR3WUrqYAPrVZBSVwymqh7auNW7",
"GMPWaPPrCeZPse5kwSR3WUrqYAPrVZBSVwymqh7auNW7",
].map((address) => new PublicKey(address));
const amount = bn(111); // each recipient will receive 111 tokens
const recipients = [PAYER_KEYPAIR.publicKey].map(
(address) => new PublicKey(address)
);
const activeStateTrees = await connection.getStateTreeInfos();

/// Pick a new tree for each transaction!
Expand Down Expand Up @@ -79,28 +77,6 @@ const PAYER_KEYPAIR = Keypair.fromSecretKey(
});
instructions.push(compressInstruction);

// Creates a cPDA for a given set of recipients. This lets you retry txns without handling spends client-side.
// The whole txn will fail if the same set of seeds (with the same order) is used a second time.
instructions.push(
await createIdempotentAirdropInstruction(
connection,
payer.publicKey,
mintAddress,
recipients,
treeInfo
)
);

// Use zk-compression LUT for your network
// https://www.zkcompression.com/developers/protocol-addresses-and-urls#lookup-tables
const lookupTableAddress = new web3.PublicKey(
"9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ" // mainnet
);
// Get the lookup table account state
const lookupTableAccount = (
await connection.getAddressLookupTable(lookupTableAddress)
).value!;

// Sign the transaction with the payer and owner keypair
const owner = payer;
const additionalSigners = dedupeSigner(payer, [owner]);
Expand All @@ -111,8 +87,7 @@ const PAYER_KEYPAIR = Keypair.fromSecretKey(
instructions,
payer,
blockhash,
additionalSigners,
[lookupTableAccount]
additionalSigners
);

const txId = await sendAndConfirmTx(connection, tx);
Expand Down
9 changes: 8 additions & 1 deletion src/scripts/connection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Rpc } from "@lightprotocol/stateless.js";
import { RPC_ENDPOINT } from "../constants";
import { RPC_ENDPOINT, PAYER_KEYPAIR } from "../constants";

const stateless = require("@lightprotocol/stateless.js");

const connection: Rpc = stateless.createRpc(RPC_ENDPOINT, RPC_ENDPOINT);
Expand All @@ -10,4 +11,10 @@ const connection: Rpc = stateless.createRpc(RPC_ENDPOINT, RPC_ENDPOINT);

const health = await connection.getIndexerHealth();
console.log("Indexer Health:", health);

console.log("connection", connection.rpcEndpoint);
const accs = await connection.getCompressedTokenAccountsByOwner(
PAYER_KEYPAIR.publicKey
);
console.log("compressed token accounts: ", accs);
})();