Skip to content

Instantly share code, notes, and snippets.

@afsardo
Created April 8, 2024 01:29
Show Gist options
  • Save afsardo/02256f1396032e3fd70a965469175a2c to your computer and use it in GitHub Desktop.
Save afsardo/02256f1396032e3fd70a965469175a2c to your computer and use it in GitHub Desktop.
import {
AccountMeta,
ComputeBudgetProgram,
Connection,
Keypair,
LAMPORTS_PER_SOL,
PublicKey,
SystemProgram,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
import * as anchor from "@coral-xyz/anchor";
import * as bip39 from "bip39";
import { derivePath } from "ed25519-hd-key";
import { sign } from "tweetnacl";
import { Domain, ExecuteReceiveMessageTxParams } from "@/types";
import { sleep } from "@/helpers";
import { MessageTransmitter } from "./contracts/types/message_transmitter";
import MessageTransmitterJSON from "./contracts/idl/message_transmitter.json";
import { TokenMessengerMinter } from "./contracts/types/token_messenger_minter";
import TokenMessengerMinterJSON from "./contracts/idl/token_messenger_minter.json";
import { BURN_TOKENS, MESSAGE_TRANSMITTER, TOKEN_MESSENGER_MINTER, USDC_ADDRESS } from "./constants";
import { findProgramAddress, hexToSolanaAddress, nonceAccount } from "./helpers";
function findPDAS({
originDomain,
messageTransmitterProgram,
tokenMessengerMinterProgram,
sourceUsdcAddress,
nonce,
}: {
originDomain: Domain;
messageTransmitterProgram: anchor.Program<MessageTransmitter>;
tokenMessengerMinterProgram: anchor.Program<TokenMessengerMinter>;
sourceUsdcAddress: string;
nonce: number;
}) {
const sourceUsdc = new PublicKey(Buffer.from(sourceUsdcAddress, "hex"));
const solanaUsdc = new PublicKey(USDC_ADDRESS);
const messageTransmitterAccount = findProgramAddress("message_transmitter", messageTransmitterProgram.programId);
const tokenMessenger = findProgramAddress("token_messenger", tokenMessengerMinterProgram.programId);
const tokenMinter = findProgramAddress("token_minter", tokenMessengerMinterProgram.programId);
const localToken = findProgramAddress("local_token", tokenMessengerMinterProgram.programId, [solanaUsdc]);
const remoteTokenMessengerKey = findProgramAddress("remote_token_messenger", tokenMessengerMinterProgram.programId, [
String(originDomain),
]);
const tokenPair = findProgramAddress("token_pair", tokenMessengerMinterProgram.programId, [
String(originDomain),
sourceUsdc,
]);
const custodyTokenAccount = findProgramAddress("custody", tokenMessengerMinterProgram.programId, [solanaUsdc]);
const authorityPda = findProgramAddress("message_transmitter_authority", messageTransmitterProgram.programId, [
tokenMessengerMinterProgram.programId,
]).publicKey;
const usedNonces = nonceAccount(BigInt(nonce), String(originDomain), messageTransmitterProgram.programId);
const eventAuthority = findProgramAddress("__event_authority", messageTransmitterProgram.programId);
const tokenMessengerEventAuthority = findProgramAddress("__event_authority", tokenMessengerMinterProgram.programId);
return {
messageTransmitterAccount,
tokenMessenger,
tokenMinter,
localToken,
remoteTokenMessengerKey,
tokenPair,
custodyTokenAccount,
authorityPda,
usedNonces,
eventAuthority,
tokenMessengerEventAuthority,
};
}
export async function executeReceiveMessageTx(params: ExecuteReceiveMessageTxParams): Promise<string> {
console.log(`Executing receive message...`);
if (!process.env.SOLANA_MNEMONIC) {
throw new Error("Missing solana relayer mnemonic");
}
const seed = bip39.mnemonicToSeedSync(process.env.SOLANA_MNEMONIC as string, "");
const keypair = Keypair.fromSeed(derivePath(`m/44'/501'/0'/0'`, seed.toString("hex")).key);
console.log("Relayer Wallet: ", keypair.publicKey.toBase58());
const rpc = process.env.SOLANA_RPC;
if (!rpc) {
throw new Error("Env SOLANA_RPC not set");
}
const connection = new Connection(rpc);
const solBalance = await connection.getBalance(keypair.publicKey);
console.log(`Balance: ${solBalance / LAMPORTS_PER_SOL} SOL`);
const wallet = new anchor.Wallet(keypair);
const anchorProvider = new anchor.AnchorProvider(connection, wallet, {
preflightCommitment: connection.commitment,
});
const tokenMessengerMinterProgram = new anchor.Program<TokenMessengerMinter>(
JSON.parse(JSON.stringify(TokenMessengerMinterJSON)),
new PublicKey(TOKEN_MESSENGER_MINTER),
anchorProvider,
);
const messageTransmitterProgram = new anchor.Program<MessageTransmitter>(
JSON.parse(JSON.stringify(MessageTransmitterJSON)),
new PublicKey(MESSAGE_TRANSMITTER),
anchorProvider,
);
if (!params.burn.mint_recipient) {
throw new Error("Couldn't find Mint Recipient in burn tx");
}
const destinationAddress = Buffer.from(params.burn.mint_recipient, "base64");
const receiver = new PublicKey(hexToSolanaAddress(`0x${destinationAddress.toString("hex")}`));
console.log("Receiver Wallet: ", receiver.toBase58());
const burnToken = BURN_TOKENS[params.burn.origin_domain];
if (!burnToken) {
throw new Error(`Couldn't find Burn Token for domain: ${params.burn.origin_domain}`);
}
const {
messageTransmitterAccount,
tokenMessenger,
tokenMinter,
localToken,
remoteTokenMessengerKey,
tokenPair,
custodyTokenAccount,
authorityPda,
usedNonces,
tokenMessengerEventAuthority,
} = findPDAS({
originDomain: params.burn.origin_domain,
messageTransmitterProgram,
tokenMessengerMinterProgram,
sourceUsdcAddress: burnToken,
nonce: Number(params.attestation.eventNonce),
});
const accountMetas: AccountMeta[] = [];
accountMetas.push({
isSigner: false,
isWritable: false,
pubkey: tokenMessenger.publicKey,
});
accountMetas.push({
isSigner: false,
isWritable: false,
pubkey: remoteTokenMessengerKey.publicKey,
});
accountMetas.push({
isSigner: false,
isWritable: true,
pubkey: tokenMinter.publicKey,
});
accountMetas.push({
isSigner: false,
isWritable: true,
pubkey: localToken.publicKey,
});
accountMetas.push({
isSigner: false,
isWritable: false,
pubkey: tokenPair.publicKey,
});
accountMetas.push({
isSigner: false,
isWritable: true,
pubkey: receiver,
});
accountMetas.push({
isSigner: false,
isWritable: true,
pubkey: custodyTokenAccount.publicKey,
});
accountMetas.push({
isSigner: false,
isWritable: false,
pubkey: TOKEN_PROGRAM_ID,
});
accountMetas.push({
isSigner: false,
isWritable: false,
pubkey: tokenMessengerEventAuthority.publicKey,
});
accountMetas.push({
isSigner: false,
isWritable: false,
pubkey: tokenMessengerMinterProgram.programId,
});
const receiveMessageTx = await messageTransmitterProgram.methods
.receiveMessage({
message: Buffer.from(params.attestation.message.replace("0x", ""), "hex"),
attestation: Buffer.from(params.attestation.attestation.replace("0x", ""), "hex"),
})
.accounts({
payer: wallet.publicKey,
caller: wallet.publicKey,
authorityPda,
messageTransmitter: messageTransmitterAccount.publicKey,
usedNonces,
receiver: tokenMessengerMinterProgram.programId,
systemProgram: SystemProgram.programId,
})
.remainingAccounts(accountMetas)
.instruction();
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
units: 200_000,
});
const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 20_000,
});
const instructions = [receiveMessageTx, modifyComputeUnits, addPriorityFee];
const latestBlockhash = await connection.getLatestBlockhash("confirmed");
const messageV0 = new TransactionMessage({
payerKey: wallet.publicKey,
recentBlockhash: latestBlockhash.blockhash,
instructions: instructions,
}).compileToV0Message();
const tx = new VersionedTransaction(messageV0);
const signature = sign.detached(messageV0.serialize(), wallet.payer.secretKey);
tx.addSignature(wallet.publicKey, Buffer.from(signature));
let txHash = null;
let attempts = 0;
let success = false;
do {
console.log(`Executing receive message (Attempt: ${attempts})...`);
txHash = await connection.sendTransaction(tx, {
skipPreflight: true,
maxRetries: 0,
});
await sleep(1000);
const txData = await connection.getTransaction(txHash, {
maxSupportedTransactionVersion: 0,
commitment: "confirmed",
});
if (txData?.slot) {
success = true;
break;
}
attempts++;
} while (attempts < 30);
if (!success) {
await sleep(3000);
const txData = await connection.getTransaction(txHash, {
maxSupportedTransactionVersion: 0,
commitment: "confirmed",
});
if (!txData) {
throw new Error("Transaction has expired: block height exceeded.");
}
success = true;
}
console.log(`Transaction: ${txHash}`);
return txHash;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment