diff --git a/programs/protocol-contracts-solana/src/lib.rs b/programs/protocol-contracts-solana/src/lib.rs index f857bc6..d49454e 100644 --- a/programs/protocol-contracts-solana/src/lib.rs +++ b/programs/protocol-contracts-solana/src/lib.rs @@ -48,6 +48,7 @@ pub mod gateway { Ok(()) } + // admin function to pause or unpause deposit pub fn set_deposit_paused(ctx: Context, deposit_paused: bool) -> Result<()> { let pda = &mut ctx.accounts.pda; require!( @@ -84,7 +85,15 @@ pub mod gateway { Ok(()) } - pub fn deposit(ctx: Context, amount: u64, receiver: [u8; 20]) -> Result<()> { + // deposit SOL into this program and the `receiver` on ZetaChain zEVM + // will get corresponding ZRC20 credit. + // amount: amount of lamports (10^-9 SOL) to deposit + // receiver: ethereum address (20Bytes) of the receiver on ZetaChain zEVM + pub fn deposit( + ctx: Context, + amount: u64, + receiver: [u8; 20], // not used in this program; for directing zetachain protocol only + ) -> Result<()> { let pda = &mut ctx.accounts.pda; require!(!pda.deposit_paused, Errors::DepositPaused); @@ -106,6 +115,10 @@ pub mod gateway { Ok(()) } + // deposit SOL into this program and the `receiver` on ZetaChain zEVM + // will get corresponding ZRC20 credit. The `receiver` should be a contract + // on zEVM and the `message` will be used as input data for the contract call. + // The `receiver` contract on zEVM will get the SOL ZRC20 credit and receive the `message`. pub fn deposit_and_call( ctx: Context, amount: u64, @@ -113,36 +126,20 @@ pub mod gateway { message: Vec, ) -> Result<()> { require!(message.len() <= 512, Errors::MemoLengthExceeded); - - let pda = &mut ctx.accounts.pda; - require!(!pda.deposit_paused, Errors::DepositPaused); - - let cpi_context = CpiContext::new( - ctx.accounts.system_program.to_account_info(), - system_program::Transfer { - from: ctx.accounts.signer.to_account_info().clone(), - to: ctx.accounts.pda.to_account_info().clone(), - }, - ); - system_program::transfer(cpi_context, amount)?; - msg!( - "{:?} deposits {:?} lamports to PDA and call contract {:?} with message {:?}", - ctx.accounts.signer.key(), - amount, - receiver, - message, - ); - + deposit(ctx, amount, receiver)?; Ok(()) } + // deposit SPL token into this program and the `receiver` on ZetaChain zEVM + // will get corresponding ZRC20 credit. + // amount: amount of SPL token to deposit + // receiver: ethereum address (20Bytes) of the receiver on ZetaChain zEVM + #[allow(unused)] pub fn deposit_spl_token( ctx: Context, amount: u64, - memo: Vec, + receiver: [u8; 20], // unused in this program; for directing zetachain protocol only ) -> Result<()> { - require!(memo.len() >= 20, Errors::MemoLengthTooShort); - require!(memo.len() <= 512, Errors::MemoLengthExceeded); let token = &ctx.accounts.token_program; let from = &ctx.accounts.from; @@ -174,7 +171,25 @@ pub mod gateway { Ok(()) } - // only tss address stored in PDA can call this instruction + // like `deposit_spl_token` instruction, + // deposit SPL token into this program and the `receiver` on ZetaChain zEVM + // will get corresponding ZRC20 credit. The `receiver` should be a contract + // on zEVM and the `message` will be used as input data for the contract call. + // The `receiver` contract on zEVM will get the SPL token ZRC20 credit and receive the `message`. + #[allow(unused)] + pub fn deposit_spl_token_and_call( + ctx: Context, + amount: u64, + receiver: [u8; 20], + message: Vec, + ) -> Result<()> { + require!(message.len() <= 512, Errors::MemoLengthExceeded); + deposit_spl_token(ctx, amount, receiver)?; + Ok(()) + } + + // require tss address signature on the internal message defined in the following + // concatenated_buffer vec. pub fn withdraw( ctx: Context, amount: u64, @@ -216,7 +231,8 @@ pub mod gateway { Ok(()) } - // only tss address stored in PDA can call this instruction + // require tss address signature on the internal message defined in the following + // concatenated_buffer vec. pub fn withdraw_spl_token( ctx: Context, amount: u64, diff --git a/tests/protocol-contracts-solana.ts b/tests/protocol-contracts-solana.ts index 8fa8cd4..33c209b 100644 --- a/tests/protocol-contracts-solana.ts +++ b/tests/protocol-contracts-solana.ts @@ -139,25 +139,20 @@ describe("some tests", () => { 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]); + + let acct = await spl.getAccount(conn, pda_ata.address); + let bal0 = acct.amount; + await gatewayProgram.methods.depositSplToken(new anchor.BN(1_000_000), Array.from(address)).accounts({ + from: tokenAccount.address, + to: pda_ata.address, + }).rpc({commitment: 'confirmed'}); + acct = await spl.getAccount(conn, pda_ata.address); + let bal1 = acct.amount; + expect(bal1-bal0).to.be.eq(1_000_000n); try { - await gatewayProgram.methods.depositSplToken(new anchor.BN(1_000_000), address).accounts( + await gatewayProgram.methods.depositSplToken(new anchor.BN(1_000_000), Array.from(address)).accounts( { from: tokenAccount.address, to: wallet_ata, @@ -169,12 +164,31 @@ describe("some tests", () => { expect(err.message).to.include("DepositToAddressMismatch"); // console.log("Error message: ", err.message); } + + // test depositSplTokenAndCall + acct = await spl.getAccount(conn, pda_ata.address); + bal0 = acct.amount; + await gatewayProgram.methods.depositSplTokenAndCall(new anchor.BN(2_000_000), Array.from(address), Buffer.from('hi', 'utf-8')).accounts({ + from: tokenAccount.address, + to: pda_ata.address, + }).rpc({commitment: 'confirmed'}); + acct = await spl.getAccount(conn, pda_ata.address); + bal1 = acct.amount; + expect(bal1-bal0).to.be.eq(2_000_000n); + + // try { + // await gatewayProgram.methods.depositSplTokenAndCall(new anchor.BN(1_000_000), Array.from(address), Buffer.from("hello", "utf-8")).accounts({ + // from: tokenAccount.address, + // to: pda_ata.address, + // }).rpc(); + // + // } }); 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()); + // 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); @@ -212,7 +226,7 @@ describe("some tests", () => { }).rpc(); const account3 = await spl.getAccount(conn, pda_ata.address); - expect(account3.amount).to.be.eq(500_000n); + expect(account3.amount-account2.amount).to.be.eq(-500_000n); try { @@ -227,7 +241,7 @@ describe("some tests", () => { 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); + expect(account4.amount).to.be.eq(2_500_000n); } }); @@ -288,11 +302,14 @@ describe("some tests", () => { it("deposit and call", async () => { let bal1 = await conn.getBalance(pdaAccount); - await gatewayProgram.methods.depositAndCall(new anchor.BN(1_000_000_000), Array.from(address), Buffer.from("hello", "utf-8")).accounts({pda: pdaAccount}).rpc(); + const txsig = await gatewayProgram.methods.depositAndCall(new anchor.BN(1_000_000_000), Array.from(address), Buffer.from("hello", "utf-8")).accounts({pda: pdaAccount}).rpc({commitment: 'confirmed'}); + const tx = await conn.getParsedTransaction(txsig, 'confirmed'); + console.log("deposit and call parsed tx", tx); let bal2 = await conn.getBalance(pdaAccount); expect(bal2-bal1).to.be.gte(1_000_000_000); }) + it("update TSS address", async () => { const newTss = new Uint8Array(20); randomFillSync(newTss); @@ -326,13 +343,12 @@ describe("some tests", () => { // now try deposit, should fail try { - await gatewayProgram.methods.deposit(new anchor.BN(1_000_000), Array.from(address)).accounts({pda: pdaAccount}).rpc(); + await gatewayProgram.methods.depositAndCall(new anchor.BN(1_000_000), Array.from(address), Buffer.from('hi', 'utf-8')).accounts({pda: pdaAccount}).rpc(); } catch (err) { console.log("Error message: ", err.message); expect(err).to.be.instanceof(anchor.AnchorError); expect(err.message).to.include("DepositPaused"); } - }); it("update authority", async () => {