# lib.rs lib.rs is the application contract on Solana that handles communication with the Wormhole Core and Token Bridge. Start by importing all necessary dependencies and declare the contract ID. ```rs use anchor_lang::prelude::*; use mpl_token_metadata::instruction::create_metadata_accounts_v3; use mpl_token_metadata::ID as metadata_program_id; use anchor_lang::solana_program::program::invoke_signed; use anchor_lang::solana_program::instruction::{Instruction, AccountMeta}; use anchor_lang::solana_program::{sysvar::rent, system_program}; use anchor_spl::token::{ID as spl_id, mint_to, approve, MintTo, Approve}; use sha3::Digest; use byteorder::{ BigEndian, WriteBytesExt, }; use std::io::{ Cursor, Write, }; use std::str::FromStr; mod account; mod context; mod constant; mod error; mod event; mod portal; mod wormhole; use account::*; use context::*; use wormhole::*; use portal::*; use error::*; use constant::*; declare_id!("BHz6MJGvo8PJaBFqaxyzgJYdY6o8h1rBgsRrUmnHCU9k"); ``` ## Initialize This initializes all of the accounts that this program will access. ```rust pub fn initialize(ctx: Context, name: String, symbol:String, uri: String) -> Result<()> { ctx.accounts.config.owner = ctx.accounts.owner.key(); ctx.accounts.config.nonce = 0; ctx.accounts.mint_authority.mint = ctx.accounts.mint.key(); // Create Metadata Accounts for SPL Token let ix = &create_metadata_accounts_v3( metadata_program_id, ctx.accounts.metadata_account.key(), ctx.accounts.mint.key(), ctx.accounts.mint_authority.key(), ctx.accounts.owner.key(), ctx.accounts.mint_authority.key(), name, symbol, uri, None, 0, true, true, None, None, None ); let accounts = vec![ ctx.accounts.metadata_program.to_account_info(), ctx.accounts.metadata_account.to_account_info(), ctx.accounts.mint.to_account_info(), ctx.accounts.mint_authority.to_account_info(), ctx.accounts.owner.to_account_info(), ctx.accounts.mint_authority.to_account_info(), ctx.accounts.system_program.to_account_info(), ctx.accounts.rent.to_account_info() ]; let seeds:&[&[u8]] = &[ b"mint_authority", &[*ctx.bumps.get("mint_authority").unwrap()] ]; invoke_signed( ix, accounts.as_slice(), &[&seeds[..]] )?; Ok(()) } ``` ## register_chain It's typically a good idea to register and track the contracts from foreign chains that you're accepting VAAs from, as anyone could deploy a contract and generate a fake VAA that looks like a real VAA you'd want to accept. ```rust //Register Application Contracts pub fn register_chain(ctx:Context, chain_id:u16, emitter_addr:Vec) -> Result<()> { ctx.accounts.emitter_acc.emitter_chain = chain_id; ctx.accounts.emitter_acc.emitter_address = emitter_addr.as_slice().try_into().unwrap(); Ok(()) } ``` ## submit_foriegn_purchase This takes in a bytes payload and calls the Wormhole Token Bridge to receive the bridged tokens and mint the corresponding amount of native token. ```rust pub fn submit_foreign_purchase(ctx:Context) -> Result<()> { // Fetch the VAA //Hash a VAA Extract and derive a VAA Key let vaa = PostedMessageData::try_from_slice(&ctx.accounts.core_bridge_vaa.data.borrow())?.0; let vaa_hash: [u8; 32] = vaa_hash(ctx.accounts.core_bridge_vaa.clone()); let (vaa_key, _) = Pubkey::find_program_address(&[ b"PostedVAA", &vaa_hash ], &Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap()); if ctx.accounts.core_bridge_vaa.key() != vaa_key { return err!(XmintError::VAAKeyMismatch); } // Already checked that the SignedVaa is owned by core bridge in account constraint logic //Check that the emitter chain and address match up with the vaa // This VAA will be from Portal Token Bridge so use the Emitter Portal Addr let emitter: EmitterAddrAccount = EmitterAddrAccount::try_from_slice(&ctx.accounts.emitter_acc.data.borrow()).unwrap(); if vaa.emitter_chain != emitter.emitter_chain || vaa.emitter_address != emitter.emitter_address { return err!(XmintError::VAAEmitterMismatch) } // Complete Transfer of P3 on Portal let complete_wrapped_ix = Instruction { program_id: Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap(), accounts: vec![ AccountMeta::new(ctx.accounts.payer.key(), true), AccountMeta::new_readonly(ctx.accounts.token_bridge_config.key(), false), AccountMeta::new_readonly(ctx.accounts.core_bridge_vaa.key(), false), AccountMeta::new(ctx.accounts.token_bridge_claim_key.key(), false), AccountMeta::new_readonly(ctx.accounts.emitter_acc.key(), false), AccountMeta::new(ctx.accounts.weth_ata_account.key(), false), // to AccountMeta::new_readonly(ctx.accounts.redeemer.key(), true), // to owner AccountMeta::new(ctx.accounts.fee_recipient.key(), false), AccountMeta::new(ctx.accounts.weth_mint.key(), false), AccountMeta::new_readonly(ctx.accounts.weth_mint_wrapped_meta.key(), false), AccountMeta::new_readonly(ctx.accounts.mint_authority_wrapped.key(), false), AccountMeta::new_readonly(rent::id(), false), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(ctx.accounts.core_bridge.key(), false), AccountMeta::new_readonly(spl_id, false), ], data: ( crate::portal::Instruction::CompleteWrappedWithPayload, {} ).try_to_vec()? }; let accounts = vec![ ctx.accounts.payer.to_account_info(), ctx.accounts.token_bridge_config.to_account_info(), ctx.accounts.core_bridge_vaa.to_account_info(), ctx.accounts.token_bridge_claim_key.to_account_info(), ctx.accounts.emitter_acc.to_account_info(), ctx.accounts.weth_ata_account.to_account_info(), //to ctx.accounts.redeemer.to_account_info(), // to_owner ctx.accounts.fee_recipient.to_account_info(), ctx.accounts.weth_mint.to_account_info(), ctx.accounts.weth_mint_wrapped_meta.to_account_info(), ctx.accounts.mint_authority_wrapped.to_account_info(), ctx.accounts.rent_account.to_account_info(), ctx.accounts.system_program.to_account_info(), ctx.accounts.core_bridge.to_account_info(), ctx.accounts.spl_program.to_account_info() ]; let complete_p3_seeds:&[&[u8]] = &[ b"redeemer", &[*ctx.bumps.get("redeemer").unwrap()] ]; invoke_signed( &complete_wrapped_ix, &accounts, &[&complete_p3_seeds[..]] )?; //// Figure out how many tokens to mint based on p3 payload let payload: PayloadTransferWithPayload = PayloadTransferWithPayload::deserialize(&mut vaa.payload.as_slice())?; ctx.accounts.receipt.amt_to_mint = payload.amount.as_u64() * 100; ctx.accounts.receipt.foreign_chain = vaa.emitter_chain; ctx.accounts.receipt.foreign_receipient = payload.payload.as_slice().try_into().unwrap(); ctx.accounts.receipt.claimed = false; Ok(()) } ``` ## claim_foreign_purchase Due to account constraints of Solana, this will take the amount of tokens that should be minted in the previous step and call the Wormhole Token Bridge to send the tokens back to the user. ```rust pub fn claim_foreign_purchase(ctx:Context) -> Result<()> { if ctx.accounts.receipt.claimed { return err!(XmintError::ReceiptClaimed); } let mint_seeds:&[&[u8]] = &[ b"mint_authority", &[*ctx.bumps.get("xmint_authority").unwrap()] ]; mint_to( CpiContext { accounts: MintTo { mint: ctx.accounts.xmint_token_mint.to_account_info(), authority: ctx.accounts.xmint_authority.to_account_info(), to: ctx.accounts.xmint_ata_account.to_account_info(), }, remaining_accounts: vec![], program: ctx.accounts.spl_program.to_account_info(), signer_seeds: &[&mint_seeds[..]] }, ctx.accounts.receipt.amt_to_mint )?; // Delgate transfer authority to Token Bridge for the newly minted tokens approve( CpiContext::new_with_signer( ctx.accounts.spl_program.to_account_info(), Approve { to: ctx.accounts.xmint_ata_account.to_account_info(), delegate: ctx.accounts.token_bridge_authority_signer.to_account_info(), authority: ctx.accounts.xmint_authority.to_account_info() }, &[&mint_seeds[..]] ), ctx.accounts.receipt.amt_to_mint )?; // Transfer tokens from Contract PDA to P1 on Portal // Instruction let transfer_ix = Instruction { program_id: Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap(), accounts: vec![ AccountMeta::new(ctx.accounts.payer.key(), true), AccountMeta::new_readonly(ctx.accounts.token_bridge_config.key(), false), AccountMeta::new(ctx.accounts.xmint_ata_account.key(), false), AccountMeta::new(ctx.accounts.xmint_token_mint.key(), false), AccountMeta::new(ctx.accounts.token_bridge_mint_custody.key(), false), AccountMeta::new_readonly(ctx.accounts.token_bridge_authority_signer.key(), false), AccountMeta::new_readonly(ctx.accounts.token_bridge_custody_signer.key(), false), AccountMeta::new(ctx.accounts.core_bridge_config.key(), false), AccountMeta::new(ctx.accounts.xmint_transfer_msg_key.key(), true), AccountMeta::new_readonly(ctx.accounts.token_bridge_emitter.key(), false), AccountMeta::new(ctx.accounts.token_bridge_sequence_key.key(), false), AccountMeta::new(ctx.accounts.core_bridge_fee_collector.key(), false), AccountMeta::new_readonly(ctx.accounts.clock.key(), false), // Dependencies AccountMeta::new_readonly(ctx.accounts.rent_account.key(), false), AccountMeta::new_readonly(ctx.accounts.system_program.key(), false), // Program AccountMeta::new_readonly(ctx.accounts.core_bridge.key(), false), AccountMeta::new_readonly(ctx.accounts.spl_program.key(), false), ], data: ( crate::portal::Instruction::TransferNative, TransferNativeData { nonce: ctx.accounts.config.nonce, amount: ctx.accounts.receipt.amt_to_mint, fee: 0, target_address: ctx.accounts.receipt.foreign_receipient, target_chain: ctx.accounts.receipt.foreign_chain } ).try_to_vec()? }; // Accounts let transfer_accs = vec![ ctx.accounts.payer.to_account_info(), ctx.accounts.token_bridge_config.to_account_info(), ctx.accounts.xmint_ata_account.to_account_info(), ctx.accounts.xmint_token_mint.to_account_info(), ctx.accounts.token_bridge_mint_custody.to_account_info(), ctx.accounts.token_bridge_authority_signer.to_account_info(), ctx.accounts.token_bridge_custody_signer.to_account_info(), ctx.accounts.core_bridge_config.to_account_info(), ctx.accounts.xmint_transfer_msg_key.to_account_info(), ctx.accounts.token_bridge_emitter.to_account_info(), ctx.accounts.token_bridge_sequence_key.to_account_info(), ctx.accounts.core_bridge_fee_collector.to_account_info(), ctx.accounts.clock.to_account_info(), // Dependencies ctx.accounts.rent_account.to_account_info(), ctx.accounts.system_program.to_account_info(), // Program ctx.accounts.core_bridge.to_account_info(), ctx.accounts.spl_program.to_account_info(), ]; // Signer Seeds let xmint_authority_seeds:&[&[u8]] = &[ b"mint_authority", &[*ctx.bumps.get("xmint_authority").unwrap()] ]; invoke_signed( &transfer_ix, &transfer_accs, &[&xmint_authority_seeds[..]] )?; ctx.accounts.config.nonce += 1; ctx.accounts.receipt.claimed = true; Ok(()) } ``` ## serialize_vaa This helpfer function takes a message payload and serializes it into a Wormhole Guardian compliant structure. ```rust pub fn serialize_vaa(vaa: &MessageData) -> Vec { let mut v = Cursor::new(Vec::new()); v.write_u32::(vaa.vaa_time).unwrap(); v.write_u32::(vaa.nonce).unwrap(); v.write_u16::(vaa.emitter_chain.clone() as u16).unwrap(); v.write(&vaa.emitter_address).unwrap(); v.write_u64::(vaa.sequence).unwrap(); v.write_u8(vaa.consistency_level).unwrap(); v.write(&vaa.payload).unwrap(); v.into_inner() } ``` ## vaa_hash This helper function takes the serialized output from the previous helper function and hash it to be verified by a Wormhole Guardian. ```rust pub fn vaa_hash(vaa: AccountInfo) -> [u8; 32] { let vaa = PostedMessageData::try_from_slice(&vaa.data.borrow()).unwrap().0; let serialized_vaa = serialize_vaa(&vaa); let mut h = sha3::Keccak256::default(); h.write(serialized_vaa.as_slice()).unwrap(); let vaa_hash: [u8; 32] = h.finalize().into(); return vaa_hash; } ```