diff --git a/programs/protocol-contracts-solana/src/lib.rs b/programs/protocol-contracts-solana/src/lib.rs index b85a2cd..280f493 100644 --- a/programs/protocol-contracts-solana/src/lib.rs +++ b/programs/protocol-contracts-solana/src/lib.rs @@ -1,9 +1,8 @@ use anchor_lang::prelude::*; use anchor_lang::system_program; -use anchor_spl::token::{transfer, Token, TokenAccount}; +use anchor_spl::token::{Token, TokenAccount}; use solana_program::keccak::hash; use solana_program::secp256k1_recover::secp256k1_recover; -use spl_associated_token_account; use std::mem::size_of; #[error_code] @@ -109,44 +108,6 @@ pub mod gateway { Ok(()) } - pub fn deposit_spl_token( - ctx: Context, - amount: u64, - memo: Vec, - ) -> Result<()> { - require!(memo.len() >= 20, Errors::MemoLengthTooShort); - require!(memo.len() <= 512, Errors::MemoLengthExceeded); - let token = &ctx.accounts.token_program; - let from = &ctx.accounts.from; - - let pda = &mut ctx.accounts.pda; - require!(!pda.deposit_paused, Errors::DepositPaused); - - let pda_ata = spl_associated_token_account::get_associated_token_address( - &ctx.accounts.pda.key(), - &from.mint, - ); - // must deposit to the ATA from PDA in order to receive credit - require!( - pda_ata == ctx.accounts.to.to_account_info().key(), - Errors::DepositToAddressMismatch - ); - - let xfer_ctx = CpiContext::new( - token.to_account_info(), - anchor_spl::token::Transfer { - from: ctx.accounts.from.to_account_info(), - to: ctx.accounts.to.to_account_info(), - authority: ctx.accounts.signer.to_account_info(), - }, - ); - transfer(xfer_ctx, amount)?; - - msg!("deposit spl token successfully"); - - Ok(()) - } - // only tss address stored in PDA can call this instruction pub fn withdraw( ctx: Context, @@ -187,58 +148,6 @@ pub mod gateway { Ok(()) } - - // only tss address stored in PDA can call this instruction - pub fn withdraw_spl_token( - ctx: Context, - amount: u64, - signature: [u8; 64], - recovery_id: u8, - message_hash: [u8; 32], - nonce: u64, - ) -> Result<()> { - let pda = &mut ctx.accounts.pda; - // let program_id = &mut ctx.accounts - if nonce != pda.nonce { - msg!("mismatch nonce"); - return err!(Errors::NonceMismatch); - } - let mut concatenated_buffer = Vec::new(); - concatenated_buffer.extend_from_slice(&pda.chain_id.to_be_bytes()); - concatenated_buffer.extend_from_slice(&nonce.to_be_bytes()); - concatenated_buffer.extend_from_slice(&amount.to_be_bytes()); - concatenated_buffer.extend_from_slice(&ctx.accounts.to.key().to_bytes()); - require!( - message_hash == hash(&concatenated_buffer[..]).to_bytes(), - Errors::MessageHashMismatch - ); - - let address = recover_eth_address(&message_hash, recovery_id, &signature)?; // ethereum address is the last 20 Bytes of the hashed pubkey - msg!("recovered address {:?}", address); - if address != pda.tss_address { - msg!("ECDSA signature error"); - return err!(Errors::TSSAuthenticationFailed); - } - - let token = &ctx.accounts.token_program; - let signer_seeds: &[&[&[u8]]] = &[&[b"meta", &[ctx.bumps.pda]]]; - - let xfer_ctx = CpiContext::new_with_signer( - token.to_account_info(), - anchor_spl::token::Transfer { - from: ctx.accounts.from.to_account_info(), - to: ctx.accounts.to.to_account_info(), - authority: pda.to_account_info(), - }, - signer_seeds, - ); - transfer(xfer_ctx, amount)?; - msg!("withdraw spl token successfully"); - - pda.nonce += 1; - - Ok(()) - } } fn recover_eth_address( diff --git a/tests/protocol-contracts-solana.ts b/tests/protocol-contracts-solana.ts index 498b8de..5876009 100644 --- a/tests/protocol-contracts-solana.ts +++ b/tests/protocol-contracts-solana.ts @@ -52,6 +52,12 @@ describe("some tests", () => { const chain_id = 111111; const chain_id_bn = new anchor.BN(chain_id); + let seeds = [Buffer.from("meta", "utf-8")]; + [pdaAccount] = anchor.web3.PublicKey.findProgramAddressSync( + seeds, + gatewayProgram.programId, + ); + it("Initializes the program", async () => { await gatewayProgram.methods.initialize(tssAddress, chain_id_bn).rpc(); @@ -65,164 +71,6 @@ describe("some tests", () => { } }); - it("Mint a SPL USDC token", async () => { - // now deploying a fake USDC SPL Token - // 1. create a mint account - const mintRent = await spl.getMinimumBalanceForRentExemptMint(conn); - const tokenTransaction = new anchor.web3.Transaction(); - tokenTransaction.add( - anchor.web3.SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: mint.publicKey, - lamports: mintRent, - space: spl.MINT_SIZE, - programId: spl.TOKEN_PROGRAM_ID - }), - spl.createInitializeMintInstruction( - mint.publicKey, - 6, - wallet.publicKey, - null, - ) - ); - await anchor.web3.sendAndConfirmTransaction(conn, tokenTransaction, [wallet, mint]); - console.log("mint account created!", mint.publicKey.toString()); - - // 2. create token account to receive mint - tokenAccount = await spl.getOrCreateAssociatedTokenAccount( - conn, - wallet, - mint.publicKey, - wallet.publicKey, - ); - // 3. mint some tokens - const mintToTransaction = new anchor.web3.Transaction().add( - spl.createMintToInstruction( - mint.publicKey, - tokenAccount.address, - wallet.publicKey, - 10_000_000, - ) - ); - await anchor.web3.sendAndConfirmTransaction(anchor.getProvider().connection, mintToTransaction, [wallet]); - console.log("Minted 10 USDC to:", tokenAccount.address.toString()); - const account = await spl.getAccount(conn, tokenAccount.address); - console.log("Account balance:", account.amount.toString()); - console.log("Account owner: ", account.owner.toString()); - - // OK; transfer some USDC SPL token to the gateway PDA - wallet_ata = await spl.getAssociatedTokenAddress( - mint.publicKey, - wallet.publicKey, - ); - console.log(`wallet_ata: ${wallet_ata.toString()}`); - }) - - it("Deposit 1_000_000 USDC to Gateway", async () => { - let seeds = [Buffer.from("meta", "utf-8")]; - [pdaAccount] = anchor.web3.PublicKey.findProgramAddressSync( - seeds, - gatewayProgram.programId, - ); - console.log("gateway pda account", pdaAccount.toString()); - pda_ata = await spl.getOrCreateAssociatedTokenAccount( - conn, - wallet, - mint.publicKey, - pdaAccount, - true - ); - console.log("pda_ata address", pda_ata.address.toString()); - const tx = new web3.Transaction(); - const memoInst = memo.createMemoInstruction( - "this is a memo", - [wallet.publicKey], - ); - tx.add(memoInst); - const depositInst = await gatewayProgram.methods.depositSplToken( - new anchor.BN(1_000_000), address).accounts( - { - from: tokenAccount.address, - to: pda_ata.address, - } - ).instruction(); - tx.add(depositInst); - const txsig = await anchor.web3.sendAndConfirmTransaction(conn, tx, [wallet]); - - - try { - await gatewayProgram.methods.depositSplToken(new anchor.BN(1_000_000), address).accounts( - { - from: tokenAccount.address, - to: wallet_ata, - } - ).rpc(); - throw new Error("Expected error not thrown"); - } catch (err) { - expect(err).to.be.instanceof(anchor.AnchorError); - expect(err.message).to.include("DepositToAddressMismatch"); - // console.log("Error message: ", err.message); - } - }); - - it("Withdraw 500_000 USDC from Gateway with ECDSA signature", async () => { - const account2 = await spl.getAccount(conn, pda_ata.address); - expect(account2.amount).to.be.eq(1_000_000n); - // console.log("B4 withdraw: Account balance:", account2.amount.toString()); - - - const pdaAccountData = await gatewayProgram.account.pda.fetch(pdaAccount); - console.log(`pda account data: nonce ${pdaAccountData.nonce}`); - const hexAddr = bufferToHex(Buffer.from(pdaAccountData.tssAddress)); - console.log(`pda account data: tss address ${hexAddr}`); - // const message_hash = fromHexString( - // "0a1e2723bd7f1996832b7ed7406df8ad975deba1aa04020b5bfc3e6fe70ecc29" - // ); - // const signature = fromHexString( - // "58be181f57b2d56b0c252127c9874a8fbe5ebd04f7632fb3966935a3e9a765807813692cebcbf3416cb1053ad9c8c83af471ea828242cca22076dd04ddbcd253" - // ); - const amount = new anchor.BN(500_000); - const nonce = pdaAccountData.nonce; - const buffer = Buffer.concat([ - chain_id_bn.toArrayLike(Buffer, 'be', 8), - nonce.toArrayLike(Buffer, 'be', 8), - amount.toArrayLike(Buffer, 'be', 8), - wallet_ata.toBuffer(), - ]); - const message_hash = keccak256(buffer); - const signature = keyPair.sign(message_hash, 'hex'); - const { r, s, recoveryParam } = signature; - const signatureBuffer = Buffer.concat([ - r.toArrayLike(Buffer, 'be', 32), - s.toArrayLike(Buffer, 'be', 32), - ]); - - await gatewayProgram.methods.withdrawSplToken(amount, Array.from(signatureBuffer), Number(recoveryParam), Array.from(message_hash), nonce) - .accounts({ - from: pda_ata.address, - to: wallet_ata, - }).rpc(); - - const account3 = await spl.getAccount(conn, pda_ata.address); - expect(account3.amount).to.be.eq(500_000n); - - - try { - (await gatewayProgram.methods.withdrawSplToken(new anchor.BN(500_000), Array.from(signatureBuffer), Number(recoveryParam), Array.from(message_hash), nonce) - .accounts({ - from: pda_ata.address, - to: wallet_ata, - }).rpc()); - throw new Error("Expected error not thrown"); // This line will make the test fail if no error is thrown - } catch (err) { - expect(err).to.be.instanceof(anchor.AnchorError); - expect(err.message).to.include("NonceMismatch"); - const account4 = await spl.getAccount(conn, pda_ata.address); - console.log("After 2nd withdraw: Account balance:", account4.amount.toString()); - expect(account4.amount).to.be.eq(500_000n); - } - - }); it("deposit and withdraw 0.5 SOL from Gateway with ECDSA signature", async () => { await gatewayProgram.methods.deposit(new anchor.BN(1_000_000_000), address).accounts({pda: pdaAccount}).rpc();