diff --git a/README.md b/README.md index 4d62eb1..69a1603 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,15 @@ ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis ``` +The PDA account address (derived from seeds `b"meta"` and canonical bump) is +``` +2f9SLuUNb7TNeM6gzBwT4ZjbL5ZyKzzHg1Ce9yiquEjj +``` + + # Introduction + This repository hosts the smart contract (program) deployed on the Solana network to enable ZetaChain's cross-chain functionality. It consists of a single program that supports the following actions: 1. Users on the Solana network can send SOL to the program to deposit into ZetaChain, with the option to invoke a ZetaChain EVM contract. diff --git a/programs/protocol-contracts-solana/src/lib.rs b/programs/protocol-contracts-solana/src/lib.rs index e8bb085..0c0e8e8 100644 --- a/programs/protocol-contracts-solana/src/lib.rs +++ b/programs/protocol-contracts-solana/src/lib.rs @@ -34,7 +34,6 @@ declare_id!("ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis"); #[program] pub mod gateway { - use super::*; pub fn initialize( @@ -338,8 +337,9 @@ pub struct Deposit<'info> { #[account(mut)] pub signer: Signer<'info>, - #[account(mut)] + #[account(mut, seeds = [b"meta"], bump)] pub pda: Account<'info, Pda>, + pub system_program: Program<'info, System>, } @@ -348,8 +348,9 @@ pub struct DepositSplToken<'info> { #[account(mut)] pub signer: Signer<'info>, - #[account(mut, seeds = [b"meta"], bump)] + #[account(seeds = [b"meta"], bump)] pub pda: Account<'info, Pda>, + pub token_program: Program<'info, Token>, #[account(mut)] @@ -363,7 +364,7 @@ pub struct Withdraw<'info> { #[account(mut)] pub signer: Signer<'info>, - #[account(mut)] + #[account(mut, seeds = [b"meta"], bump)] pub pda: Account<'info, Pda>, /// CHECK: to account is not read so no need to check its owners; the program neither knows nor cares who the owner is. #[account(mut)] @@ -378,10 +379,9 @@ pub struct WithdrawSPLToken<'info> { #[account(mut, seeds = [b"meta"], bump)] pub pda: Account<'info, Pda>, - #[account(mut)] + #[account(mut, token::mint = mint_account, token::authority = pda)] pub pda_ata: Account<'info, TokenAccount>, // associated token address of PDA - #[account()] pub mint_account: Account<'info, Mint>, #[account(mut)] @@ -392,7 +392,7 @@ pub struct WithdrawSPLToken<'info> { #[derive(Accounts)] pub struct UpdateTss<'info> { - #[account(mut)] + #[account(mut, seeds = [b"meta"], bump)] pub pda: Account<'info, Pda>, #[account(mut)] pub signer: Signer<'info>, @@ -400,7 +400,7 @@ pub struct UpdateTss<'info> { #[derive(Accounts)] pub struct UpdateAuthority<'info> { - #[account(mut)] + #[account(mut, seeds = [b"meta"], bump)] pub pda: Account<'info, Pda>, #[account(mut)] pub signer: Signer<'info>, @@ -408,7 +408,7 @@ pub struct UpdateAuthority<'info> { #[derive(Accounts)] pub struct UpdatePaused<'info> { - #[account(mut)] + #[account(mut, seeds = [b"meta"], bump)] pub pda: Account<'info, Pda>, #[account(mut)] pub signer: Signer<'info>, diff --git a/tests/protocol-contracts-solana.ts b/tests/protocol-contracts-solana.ts index 9511e50..a50b3f7 100644 --- a/tests/protocol-contracts-solana.ts +++ b/tests/protocol-contracts-solana.ts @@ -169,7 +169,7 @@ describe("some tests", () => { await gatewayProgram.methods.depositSplToken(new anchor.BN(1_000_000), Array.from(address)).accounts({ from: tokenAccount.address, to: pda_ata.address, - }).rpc({commitment: 'confirmed'}); + }).rpc({commitment: 'processed'}); acct = await spl.getAccount(conn, pda_ata.address); let bal1 = acct.amount; expect(bal1-bal0).to.be.eq(1_000_000n); @@ -298,7 +298,7 @@ describe("some tests", () => { } catch (err) { expect(err).to.be.instanceof(anchor.AnchorError); console.log("Error message: ", err.message); - expect(err.message).to.include("SPLAtaAndMintAddressMismatch"); + expect(err.message).to.include("ConstraintTokenMint"); 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(2_500_000n); @@ -307,7 +307,7 @@ describe("some tests", () => { }); it("deposit and withdraw 0.5 SOL from Gateway with ECDSA signature", async () => { - await gatewayProgram.methods.deposit(new anchor.BN(1_000_000_000), Array.from(address)).accounts({pda: pdaAccount}).rpc(); + await gatewayProgram.methods.deposit(new anchor.BN(1_000_000_000), Array.from(address)).accounts({}).rpc(); let bal1 = await conn.getBalance(pdaAccount); console.log("pda account balance", bal1); expect(bal1).to.be.gte(1_000_000_000); @@ -341,7 +341,6 @@ describe("some tests", () => { await gatewayProgram.methods.withdraw( amount, Array.from(signatureBuffer), Number(recoveryParam), Array.from(message_hash), nonce) .accounts({ - pda: pdaAccount, to: to, }).rpc(); let bal2 = await conn.getBalance(pdaAccount); @@ -353,7 +352,7 @@ describe("some tests", () => { it("deposit and call", async () => { let bal1 = await conn.getBalance(pdaAccount); - 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 txsig = await gatewayProgram.methods.depositAndCall(new anchor.BN(1_000_000_000), Array.from(address), Buffer.from("hello", "utf-8")).accounts({}).rpc({commitment: 'confirmed'}); const tx = await conn.getParsedTransaction(txsig, 'confirmed'); console.log("deposit and call parsed tx", tx); let bal2 = await conn.getBalance(pdaAccount); @@ -375,7 +374,6 @@ describe("some tests", () => { // only the authority stored in PDA can update the TSS address; the following should fail try { await gatewayProgram.methods.updateTss(Array.from(newTss)).accounts({ - pda: pdaAccount, signer: mint.publicKey, }).signers([mint]).rpc(); } catch (err) { @@ -389,12 +387,12 @@ describe("some tests", () => { randomFillSync(newTss); // console.log("generated new TSS address", newTss); await gatewayProgram.methods.setDepositPaused(true).accounts({ - pda: pdaAccount, + }).rpc(); // now try deposit, should fail try { - await gatewayProgram.methods.depositAndCall(new anchor.BN(1_000_000), Array.from(address), Buffer.from('hi', 'utf-8')).accounts({pda: pdaAccount}).rpc(); + await gatewayProgram.methods.depositAndCall(new anchor.BN(1_000_000), Array.from(address), Buffer.from('hi', 'utf-8')).accounts({}).rpc(); } catch (err) { console.log("Error message: ", err.message); expect(err).to.be.instanceof(anchor.AnchorError); @@ -405,7 +403,7 @@ describe("some tests", () => { it("update authority", async () => { const newAuthority = anchor.web3.Keypair.generate(); await gatewayProgram.methods.updateAuthority(newAuthority.publicKey).accounts({ - pda: pdaAccount, + }).rpc(); // const pdaAccountData = await gatewayProgram.account.pda.fetch(pdaAccount); // expect(pdaAccountData.authority).to.be.eq(newAuthority.publicKey); @@ -413,7 +411,7 @@ describe("some tests", () => { // now the old authority cannot update TSS address and will fail try { await gatewayProgram.methods.updateTss(Array.from(new Uint8Array(20))).accounts({ - pda: pdaAccount, + }).rpc(); } catch (err) { console.log("Error message: ", err.message); @@ -422,6 +420,38 @@ describe("some tests", () => { } }); + it("create an account owned by the gateway program", async () => { + const gateway_id =gatewayProgram.programId; + console.log("gateway program id", gateway_id.toString()); + const fake_pda = anchor.web3.Keypair.generate(); + const rentExemption = await conn.getMinimumBalanceForRentExemption(100); + const instr1 = anchor.web3.SystemProgram.createAccount( + { + fromPubkey: wallet.publicKey, + newAccountPubkey: fake_pda.publicKey, + lamports: rentExemption, + space: 100, + programId: gatewayProgram.programId, + } + ) + const tx = new anchor.web3.Transaction(); + tx.add(instr1, ); + await anchor.web3.sendAndConfirmTransaction(conn, tx, [wallet, fake_pda]); + + const newTss = new Uint8Array(20); + randomFillSync(newTss); + // console.log("generated new TSS address", newTss); + try { + await gatewayProgram.methods.updateTss(Array.from(newTss)).accounts({ + pda: fake_pda.publicKey, + }).rpc(); + } catch (err) { + console.log("Error message: ", err.message); + expect(err).to.be.instanceof(anchor.AnchorError); + expect(err.message).to.include("AccountDiscriminatorMismatch."); + } + }); + });