Skip to content

Commit

Permalink
Gateway: add deposit SPL token instruction
Browse files Browse the repository at this point in the history
and a positive and a negative test cases
  • Loading branch information
brewmaster012 committed Jun 20, 2024
1 parent 893ea57 commit 95c7b60
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 30 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ and `cargo` package manger must be installed. The program
is built with the `anchor` framework so it needs to be
installed as well; see [installation](https://www.anchor-lang.com/docs/installation)

To build
To build (this will require at least 2GB disk space)
```bash
$ anchor build
```
Expand All @@ -67,9 +67,11 @@ $ anchor test
The Gateway program derive a PDA (Program Derived Address)
with seeds `b"meta"` and canonical bump.
This PDA account/address actually holds the SOL
balance of the Gateway program. For SPL tokens,
the PDA derived ATA (Associated Token Account) is
used to hold the SPL token balance.
balance of the Gateway program.
For SPL tokens, the program stores the SPL token
in PDA derived ATAs. For each SPL token (different mint
account), the program creates ATA from PDA and the Mint
(standard way of deriving ATA in Solana SPL program).

The PDA account itself is a data account that holds
Gateway program state, namely the following data types
Expand Down
2 changes: 1 addition & 1 deletion programs/protocol-contracts-solana/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ idl-build = ["anchor-lang/idl-build"]
anchor-lang = "0.30.0"
anchor-spl = { version = "0.30.0" , features = ["idl-build"]}
solana-program = "1.18.15"

spl-associated-token-account = "3.0.2"
51 changes: 46 additions & 5 deletions programs/protocol-contracts-solana/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use anchor_lang::system_program;
use anchor_spl::token::{transfer, 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]
Expand All @@ -15,6 +16,8 @@ pub enum Errors {
NonceMismatch,
#[msg("TSSAuthenticationFailed")]
TSSAuthenticationFailed,
#[msg("DepositToAddressMismatch")]
DepositToAddressMismatch,
}

declare_id!("9WSwbVLthCsJXABeDJcVcw4UQMYuoNLTJTLqueFXU5Q2");
Expand All @@ -35,20 +38,44 @@ pub mod gateway {
Ok(())
}

pub fn donate(ctx: Context<Donate>, amount: u64) -> Result<()> {
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
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)?;

Ok(())
}

pub fn deposit_spl_token(ctx: Context<DepositSplToken>, amount: u64) -> Result<()> {
let token = &ctx.accounts.token_program;
let from = &ctx.accounts.from;

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(())
}

pub fn withdraw(
ctx: Context<Withdraw>,
amount: u64,
Expand Down Expand Up @@ -119,7 +146,6 @@ pub mod gateway {

Ok(())
}

}

fn recover_eth_address(
Expand All @@ -145,14 +171,14 @@ pub struct Initialize<'info> {
#[account(mut)]
pub signer: Signer<'info>,

#[account(init, payer = signer, space=size_of::<Pda>() + 8, seeds=[b"meta"], bump)]
#[account(init, payer = signer, space = size_of::< Pda > () + 8, seeds = [b"meta"], bump)]
pub pda: Account<'info, Pda>,

pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Donate<'info> {
pub struct Deposit<'info> {
#[account(mut)]
pub signer: Signer<'info>,

Expand All @@ -161,6 +187,21 @@ pub struct Donate<'info> {
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct DepositSplToken<'info> {
#[account(mut)]
pub signer: Signer<'info>,

#[account(mut, seeds = [b"meta"], bump)]
pub pda: Account<'info, Pda>,
pub token_program: Program<'info, Token>,

#[account(mut)]
pub from: Account<'info, TokenAccount>, // this must be owned by signer; normally the ATA of signer
#[account(mut)]
pub to: Account<'info, TokenAccount>, // this must be ATA of PDA
}

#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(mut)]
Expand Down
65 changes: 45 additions & 20 deletions tests/protocol-contracts-solana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe("some tests", () => {
const gatewayProgram = anchor.workspace.Gateway as Program<Gateway>;
const wallet = anchor.workspace.Gateway.provider.wallet.payer;
const mint = anchor.web3.Keypair.generate();
let tokenAccount;
let tokenAccount: spl.Account;
let wallet_ata: anchor.web3.PublicKey;
let pdaAccount: anchor.web3.PublicKey;
let pda_ata: spl.Account;
Expand Down Expand Up @@ -88,7 +88,7 @@ describe("some tests", () => {
console.log(`wallet_ata: ${wallet_ata.toString()}`);
})

it("Withdraw 500_000 USDC from Gateway with ECDSA signature", async () => {
it("Deposit 1_000_000 USDC to Gateway", async () => {
let seeds = [Buffer.from("meta", "utf-8")];
[pdaAccount] = anchor.web3.PublicKey.findProgramAddressSync(
seeds,
Expand All @@ -103,14 +103,38 @@ describe("some tests", () => {
true
);
console.log("pda_ata address", pda_ata.address.toString());
const tx_xfer = await spl.transfer(
conn,
wallet,
tokenAccount.address,
pda_ata.address,
wallet,
1_000_000
);
await gatewayProgram.methods.depositSplToken(new anchor.BN(1_000_000)).accounts(
{
from: tokenAccount.address,
to: pda_ata.address,
}
)
.rpc();
try {
await gatewayProgram.methods.depositSplToken(new anchor.BN(1_000_000)).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 tx_xfer = await spl.transfer(
// conn,
// wallet,
// tokenAccount.address,
// pda_ata.address,
// wallet,
// 1_000_000
// );
// console.log("xfer tx hash", tx_xfer);
const account2 = await spl.getAccount(conn, pda_ata.address);
expect(account2.amount).to.be.eq(1_000_000n);
Expand Down Expand Up @@ -153,16 +177,17 @@ describe("some tests", () => {

});

it("withdraw 0.5 SOL from Gateway with ECDSA signature", async () => {
const transaction = new anchor.web3.Transaction();
transaction.add(
web3.SystemProgram.transfer({
fromPubkey: wallet.publicKey,
toPubkey: pdaAccount,
lamports: 1_000_000_000,
})
);
await anchor.web3.sendAndConfirmTransaction(conn, transaction, [wallet]);
it("deposit and withdraw 0.5 SOL from Gateway with ECDSA signature", async () => {
await gatewayProgram.methods.deposit(new anchor.BN(1_000_000_000)).accounts({pda:pdaAccount}).rpc();
// const transaction = new anchor.web3.Transaction();
// transaction.add(
// web3.SystemProgram.transfer({
// fromPubkey: wallet.publicKey,
// toPubkey: pdaAccount,
// lamports: 1_000_000_000,
// })
// );
// await anchor.web3.sendAndConfirmTransaction(conn, transaction, [wallet]);
let bal1 = await conn.getBalance(pdaAccount);
console.log("pda account balance", bal1);
expect(bal1).to.be.gte(1_000_000_000);
Expand Down

0 comments on commit 95c7b60

Please sign in to comment.