ITS ALIVE. solana purchase works

This commit is contained in:
spacemandev 2022-09-25 16:12:19 -05:00
parent 35c47cab60
commit 5b6dd035e1
5 changed files with 157 additions and 39 deletions

View File

@ -23,4 +23,12 @@ pub struct Redeemer {}
#[account]
pub struct MintInfo {
pub mint: Pubkey
}
#[account]
pub struct Receipt {
pub amt_to_mint: u64,
pub foreign_receipient: [u8; 32],
pub foreign_chain: u16,
pub claimed: bool
}

View File

@ -6,7 +6,7 @@ use crate::wormhole::*;
use crate::constant::*;
use std::str::FromStr;
use anchor_spl::token::ID as spl_id;
use crate::*;
#[derive(Accounts)]
pub struct Initialize<'info>{
@ -82,6 +82,7 @@ pub struct RegisterChain<'info> {
pub emitter_acc: Account<'info, EmitterAddrAccount>,
}
#[derive(Accounts)]
pub struct SubmitForeignPurchase<'info> {
#[account(mut)]
@ -93,6 +94,16 @@ pub struct SubmitForeignPurchase<'info> {
bump,
)]
pub config: Account<'info, Config>,
#[account(
init,
seeds = [
vaa_hash(core_bridge_vaa.clone()).as_slice()
],
bump,
payer = payer,
space = 64
)]
pub receipt: Account<'info, Receipt>,
// Fetch the VAA
/// CHECK: Checked in lib.rs because it requires some fancy hashing
@ -187,20 +198,37 @@ pub struct SubmitForeignPurchase<'info> {
)]
pub mint_authority_wrapped: AccountInfo<'info>,
pub rent_account: Sysvar<'info, Rent>,
/// CHECK: Make sure this is the right token bridge account
/// CHECK: Make sure this is the right core bridge account
#[account(
constraint = token_bridge_program.key() == Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap()
constraint = core_bridge.key() == Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap()
)]
pub token_bridge_program: AccountInfo<'info>,
pub core_bridge: AccountInfo<'info>,
/// CHECK: SPL Program should be actual SPL Program
#[account(
constraint = spl_program.key() == spl_id
)]
pub spl_program: AccountInfo<'info>,
/// CHECK: Make sure this is the right token bridge account
#[account(
constraint = token_bridge.key() == Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap()
)]
pub token_bridge: AccountInfo<'info>
}
#[derive(Accounts)]
pub struct ClaimForeignPurchase<'info> {
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
#[account(
mut,
seeds = [b"config"],
bump,
)]
pub config: Account<'info, Config>,
#[account(mut)]
pub receipt: Account<'info, Receipt>,
// Mint SOL#T SPL tokens to Contract PDA
@ -209,6 +237,7 @@ pub struct SubmitForeignPurchase<'info> {
#[account(
seeds=[b"mint_authority"],
bump,
mut
)]
pub xmint_authority: Account<'info, MintInfo>,
/// CHECK: TODO: Check if owned by SPL program
@ -219,7 +248,7 @@ pub struct SubmitForeignPurchase<'info> {
// P1 Portal Transfer to Recepient
#[account(
seeds = [
b"mint",
xmint_token_mint.key().to_bytes().as_slice(),
],
bump,
seeds::program = Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap(),
@ -255,6 +284,7 @@ pub struct SubmitForeignPurchase<'info> {
)]
/// CHECK: The seeds constraint should check validity
pub core_bridge_config: AccountInfo<'info>,
#[account(mut)]
pub xmint_transfer_msg_key: Signer<'info>,
#[account(
seeds=[
@ -292,4 +322,27 @@ pub struct SubmitForeignPurchase<'info> {
constraint = core_bridge.key() == Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap()
)]
pub core_bridge: AccountInfo<'info>,
/// CHECK: SPL Program should be actual SPL Program
#[account(
constraint = spl_program.key() == spl_id
)]
pub spl_program: AccountInfo<'info>,
#[account(
mut,
seeds = [b"config"],
bump,
seeds::program = Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap()
)]
/// CHECK: Token Bridge Config
pub token_bridge_config: AccountInfo<'info>,
pub rent_account: Sysvar<'info, Rent>,
/// CHECK: Make sure this is the right token bridge account
#[account(
constraint = token_bridge.key() == Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap()
)]
pub token_bridge: AccountInfo<'info>
}

View File

@ -9,4 +9,7 @@ pub enum XmintError {
#[msg("Posted VAA Emitter Chain ID or Address Mismatch")]
VAAEmitterMismatch,
#[msg("Receipt already claimed!")]
ReceiptClaimed,
}

View File

@ -4,8 +4,7 @@ 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};
use anchor_spl::token::MintTo;
use anchor_spl::token::{ID as spl_id, mint_to, approve, MintTo, Approve};
use sha3::Digest;
use byteorder::{
@ -97,17 +96,11 @@ pub mod solana {
Ok(())
}
//Submit Foreign Purchase
pub fn submit_foreign_purchase(ctx:Context<SubmitForeignPurchase>) -> Result <()> {
pub fn submit_foreign_purchase(ctx:Context<SubmitForeignPurchase>) -> 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 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();
let vaa_hash: [u8; 32] = vaa_hash(ctx.accounts.core_bridge_vaa.clone());
let (vaa_key, _) = Pubkey::find_program_address(&[
b"PostedVAA",
@ -129,7 +122,6 @@ pub mod solana {
// Complete Transfer of P3 on Portal
let complete_wrapped_ix = Instruction {
program_id: Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap(),
@ -147,7 +139,7 @@ pub mod solana {
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.token_bridge_program.key(), false),
AccountMeta::new_readonly(ctx.accounts.core_bridge.key(), false),
AccountMeta::new_readonly(spl_id, false),
],
data: (
@ -170,7 +162,7 @@ pub mod solana {
ctx.accounts.mint_authority_wrapped.to_account_info(),
ctx.accounts.rent_account.to_account_info(),
ctx.accounts.system_program.to_account_info(),
ctx.accounts.token_bridge_program.to_account_info(),
ctx.accounts.core_bridge.to_account_info(),
ctx.accounts.spl_program.to_account_info()
];
@ -185,18 +177,24 @@ pub mod solana {
&[&complete_p3_seeds[..]]
)?;
// Mint SOL#T SPL tokens to Contract PDA
//// Figure out how many tokens to mint based on p3 payload
let payload: PayloadTransferWithPayload = PayloadTransferWithPayload::deserialize(&mut vaa.payload.as_slice())?;
let amt_to_mint:u64 = payload.amount.as_u64() * 100;
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(())
}
pub fn claim_foreign_purchase(ctx:Context<ClaimForeignPurchase>) -> Result<()> {
if ctx.accounts.receipt.claimed {
return err!(XmintError::ReceiptClaimed);
}
let mint_seeds:&[&[u8]] = &[
b"mint_authority",
&[*ctx.bumps.get("mint_authority").unwrap()]
&[*ctx.bumps.get("xmint_authority").unwrap()]
];
mint_to(
@ -210,7 +208,21 @@ pub mod solana {
program: ctx.accounts.spl_program.to_account_info(),
signer_seeds: &[&mint_seeds[..]]
},
amt_to_mint
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
@ -242,10 +254,10 @@ pub mod solana {
crate::portal::Instruction::TransferNative,
TransferNativeData {
nonce: ctx.accounts.config.nonce,
amount: amt_to_mint,
amount: ctx.accounts.receipt.amt_to_mint,
fee: 0,
target_address: payload.payload.as_slice().try_into().unwrap(),
target_chain: vaa.emitter_chain
target_address: ctx.accounts.receipt.foreign_receipient,
target_chain: ctx.accounts.receipt.foreign_chain
}
).try_to_vec()?
};
@ -276,7 +288,7 @@ pub mod solana {
// Signer Seeds
let xmint_authority_seeds:&[&[u8]] = &[
b"mint_authority",
&[*ctx.bumps.get("mint_authority").unwrap()]
&[*ctx.bumps.get("xmint_authority").unwrap()]
];
invoke_signed(
@ -286,7 +298,8 @@ pub mod solana {
)?;
ctx.accounts.config.nonce += 1;
Ok(())
ctx.accounts.receipt.claimed = true;
Ok(())
}
}
@ -303,4 +316,13 @@ pub fn serialize_vaa(vaa: &MessageData) -> Vec<u8> {
v.write_u8(vaa.consistency_level).unwrap();
v.write(&vaa.payload).unwrap();
v.into_inner()
}
}
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;
}

View File

@ -488,7 +488,7 @@ export async function submitForeignPurchase(src:string, target:string, vaa:strin
);
// P1 Portal Transfer back to Recepient Accounts
const tokenBridgeMintCustody = findProgramAddressSync([Buffer.from("mint")], tokenBridgePubKey)[0];
const tokenBridgeMintCustody = findProgramAddressSync([xmintTokenMint.toBuffer()], tokenBridgePubKey)[0];
const tokenBridgeAuthoritySigner = findProgramAddressSync([Buffer.from("authority_signer")], tokenBridgePubKey)[0];
const tokenBridgeCustodySigner = findProgramAddressSync([Buffer.from("custody_signer")], tokenBridgePubKey)[0];
const coreBridgeConfig = findProgramAddressSync([Buffer.from("Bridge")], coreBridgePubKey)[0];
@ -500,7 +500,11 @@ export async function submitForeignPurchase(src:string, target:string, vaa:strin
], coreBridgePubKey)[0];
const coreBridgeFeeCollector = findProgramAddressSync([Buffer.from("fee_collector")], coreBridgePubKey)[0];
const tx = await xmint.methods
// need to split the following into two because Solana account limit
const receiptAcc = findProgramAddressSync([ Buffer.from(vaaHash, "hex") ], xmint.programId)[0];
// Submit Foreign Purchase
const submitTx = await xmint.methods
.submitForeignPurchase()
.accounts({
payer: srcKey.publicKey,
@ -509,6 +513,7 @@ export async function submitForeignPurchase(src:string, target:string, vaa:strin
coreBridgeVaa: coreBridgeVaaKey,
processedVaa: processedVaaKey,
emitterAcc: emitterAddressAcc,
receipt: receiptAcc,
tokenBridgeConfig: tokenBridgeConfigAcc,
tokenBridgeClaimKey: tokenBridgeClaimAcc,
@ -519,7 +524,32 @@ export async function submitForeignPurchase(src:string, target:string, vaa:strin
wethMintWrappedMeta: wethWrappedMeta,
mintAuthorityWrapped: mintAuthorityWrapped,
rentAccount: anchor.web3.SYSVAR_RENT_PUBKEY,
tokenBridgeProgram: tokenBridgePubKey,
coreBridge: coreBridgePubKey,
splProgram: TOKEN_PROGRAM_ID,
tokenBridge: tokenBridgePubKey
})
.preInstructions([
anchor.web3.ComputeBudgetProgram.requestUnits({
units: 1400000,
additionalFee: 0,
})
])
.rpc();
await new Promise((r) => setTimeout(r, 16000)); //wait for accounts to finialize
// Claim Foreign Purchase
const claimTx = await xmint.methods
.claimForeignPurchase()
.accounts({
payer: srcKey.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
config: configAcc,
receipt: receiptAcc,
rentAccount: anchor.web3.SYSVAR_RENT_PUBKEY,
tokenBridgeConfig: tokenBridgeConfigAcc,
splProgram: TOKEN_PROGRAM_ID,
xmintTokenMint: xmintTokenMint,
@ -535,7 +565,9 @@ export async function submitForeignPurchase(src:string, target:string, vaa:strin
tokenBridgeSequenceKey: tokenBridgeSequenceKey,
coreBridgeFeeCollector: coreBridgeFeeCollector,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
coreBridge: coreBridgePubKey
coreBridge: coreBridgePubKey,
tokenBridge: tokenBridgePubKey
})
.preInstructions([
anchor.web3.ComputeBudgetProgram.requestUnits({
@ -546,7 +578,7 @@ export async function submitForeignPurchase(src:string, target:string, vaa:strin
.signers([transferMsgKey])
.rpc();
const sfpVaa = await fetchVaa(src, tx, true);
const sfpVaa = await fetchVaa(src, claimTx, true);
console.log(sfpVaa);
return sfpVaa;
}