Created
April 8, 2024 01:29
-
-
Save afsardo/02256f1396032e3fd70a965469175a2c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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