Skip to content

Commit

Permalink
Redefine certificate_vault to be account owning all ATAs of all cer…
Browse files Browse the repository at this point in the history
…tificates
  • Loading branch information
gigileungyingchi committed Oct 24, 2023
1 parent d1c5df1 commit e1e290a
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 58 deletions.
7 changes: 4 additions & 3 deletions packages/fund-sender/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AnchorProvider, Program } from "@coral-xyz/anchor";
import * as anchor from "@coral-xyz/anchor";
import { PublicKey, SystemProgram, Connection } from "@solana/web3.js";
import TOKEN_PROGRAM_ID from "@solana/spl-token";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
import BN from "bn.js";
import { FundSender, IDL } from "../../types/fund_sender";

Expand Down Expand Up @@ -389,7 +389,8 @@ export class FundSenderClient {
*
*/
public async storeCertificates(
outputYieldTokenAccount: PublicKey
outputYieldTokenAccount: PublicKey,
certificateVaultAta: PublicKey
): Promise<FundSenderClient> {
if (!this.config) {
throw new Error("Client not initialized");
Expand All @@ -405,7 +406,7 @@ export class FundSenderClient {
state: this.stateAddress,
outputYieldAccount,
outputYieldTokenAccount,
certificateVault: this.config.certificateVault,
certificateVaultAta,
tokenProgram: TOKEN_PROGRAM_ID,
})
.rpc()
Expand Down
173 changes: 135 additions & 38 deletions packages/tests/fundSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from "@solana/web3.js";
import { FundSenderClient } from "../fund-sender/client";
import {
Account,
createMint,
getAccount,
getOrCreateAssociatedTokenAccount,
Expand Down Expand Up @@ -38,36 +37,14 @@ describe("fund-sender", () => {
context("create and update", () => {
it("can register a new fund sender state", async () => {
const destinationAccount = Keypair.generate().publicKey;
const provider = AnchorProvider.local();
const connection0 = provider.connection;
const payer = Keypair.generate();
const tx = await connection0.requestAirdrop(
payer.publicKey,
LAMPORTS_PER_SOL
);
const blockhash = await connection0.getLatestBlockhash();
await connection0.confirmTransaction({ signature: tx, ...blockhash });
const mint = await createMint(
connection0,
payer,
payer.publicKey,
null,
0
);
const certificateVault = await getOrCreateAssociatedTokenAccount(
connection0,
payer,
mint,
authority.publicKey,
true
);
const certificateVault = Keypair.generate();

client = await FundSenderClient.register(
sunriseState,
authority.publicKey,
destinationSeed,
destinationAccount,
certificateVault.address,
certificateVault.publicKey,
spendThreshold
);
});
Expand Down Expand Up @@ -168,7 +145,7 @@ describe("fund-sender", () => {

context("transfer functions", () => {
let destinationAccount: PublicKey;
let certificateVault: Account;
let certificateVault: Keypair;
let authority: Keypair;
let mint: PublicKey;
// let connection0: Connection;
Expand All @@ -192,19 +169,20 @@ describe("fund-sender", () => {
null,
10
);
certificateVault = await getOrCreateAssociatedTokenAccount(
/* certificateVault = await getOrCreateAssociatedTokenAccount(
connection0,
authority,
mint,
authority.publicKey,
true
);
); */
certificateVault = Keypair.generate();
client = await FundSenderClient.register(
sunriseState,
authority.publicKey,
destinationSeed,
destinationAccount,
certificateVault.address,
certificateVault.publicKey,
spendThreshold
);
});
Expand Down Expand Up @@ -257,10 +235,18 @@ describe("fund-sender", () => {
const blockhash3 = await connection.getLatestBlockhash();
await connection.confirmTransaction({ signature: tx3, ...blockhash3 });

await client.storeCertificates(ata.address);
const certificateVaultAta = await getOrCreateAssociatedTokenAccount(
connection,
authority,
mint,
certificateVault.publicKey,
true
);

await client.storeCertificates(ata.address, certificateVaultAta.address);
const certificateVaultInfo = await getAccount(
connection,
certificateVault.address
certificateVaultAta.address
);

expect(Number(certificateVaultInfo.amount)).to.equal(mintAmount);
Expand Down Expand Up @@ -297,12 +283,120 @@ describe("fund-sender", () => {
const blockhash3 = await connection.getLatestBlockhash();
await connection.confirmTransaction({ signature: tx3, ...blockhash3 });

const shouldFail = client.storeCertificates(ata.address);
const certificateVaultAta = await getOrCreateAssociatedTokenAccount(
connection,
authority,
mint,
certificateVault.publicKey,
true
);

const shouldFail = client.storeCertificates(
ata.address,
certificateVaultAta.address
);
return expect(shouldFail).to.be.rejectedWith(
"IncorrectTokenAccountOwner."
);
});

it("should not be able to transfer to a token account not owned by certificate_vault", async () => {
const connection = client.program.provider.connection;
const tx1 = await connection.requestAirdrop(
authority.publicKey,
LAMPORTS_PER_SOL
);
const blockhash1 = await connection.getLatestBlockhash();
await connection.confirmTransaction({ signature: tx1, ...blockhash1 });
const ata = await getOrCreateAssociatedTokenAccount(
connection,
authority,
mint,
client.getOutputYieldAccount(destinationSeed),
true
);

const mintAmount = 100;
const tx3 = await mintTo(
connection,
authority,
mint,
ata.address,
authority.publicKey,
mintAmount
);

const blockhash3 = await connection.getLatestBlockhash();
await connection.confirmTransaction({ signature: tx3, ...blockhash3 });

const unauthorisedCertificateVault = Keypair.generate();
const unauthorisedCertificateVaultAta =
await getOrCreateAssociatedTokenAccount(
connection,
authority,
mint,
unauthorisedCertificateVault.publicKey,
true
);

const shouldFail = client.storeCertificates(
ata.address,
unauthorisedCertificateVaultAta.address
);
return expect(shouldFail).to.be.rejectedWith("ConstraintTokenOwner.");
});

it("should not be able to transfer to a token account of another mint", async () => {
const connection = client.program.provider.connection;
const tx1 = await connection.requestAirdrop(
authority.publicKey,
LAMPORTS_PER_SOL
);
const blockhash1 = await connection.getLatestBlockhash();
await connection.confirmTransaction({ signature: tx1, ...blockhash1 });
const ata = await getOrCreateAssociatedTokenAccount(
connection,
authority,
mint,
client.getOutputYieldAccount(destinationSeed),
true
);

const mintAmount = 100;
const tx3 = await mintTo(
connection,
authority,
mint,
ata.address,
authority.publicKey,
mintAmount
);

const blockhash3 = await connection.getLatestBlockhash();
await connection.confirmTransaction({ signature: tx3, ...blockhash3 });

const wrongMint = await createMint(
connection,
authority,
authority.publicKey,
null,
10
);
const wrongCertificateVaultAta = await getOrCreateAssociatedTokenAccount(
connection,
authority,
wrongMint,
certificateVault.publicKey,
true
);

const shouldFail = client.storeCertificates(
ata.address,
wrongCertificateVaultAta.address
);
return expect(shouldFail).to.be.rejectedWith("ConstraintAssociated.");
});

it("should be able to update certificate vault and store certificates in new vault", async () => {
const connection = client.program.provider.connection;
const tx1 = await connection.requestAirdrop(
Expand Down Expand Up @@ -332,12 +426,12 @@ describe("fund-sender", () => {
const blockhash3 = await connection.getLatestBlockhash();
await connection.confirmTransaction({ signature: tx3, ...blockhash3 });

const anotherUser = Keypair.generate();
const newCertificateVault = await getOrCreateAssociatedTokenAccount(
const newCertificateVaultA = Keypair.generate();
const newCertificateVaultAta = await getOrCreateAssociatedTokenAccount(
connection,
authority,
mint,
anotherUser.publicKey,
newCertificateVaultA.publicKey,
true
);
const authorisedUserProvider = new AnchorProvider(
Expand All @@ -350,15 +444,18 @@ describe("fund-sender", () => {
authorisedUserProvider
);
await authorisedClient.updateCertificateVault(
newCertificateVault.address
newCertificateVaultA.publicKey
);

const updatedClient = await FundSenderClient.fetch(client.stateAddress);

await updatedClient.storeCertificates(ata.address);
await updatedClient.storeCertificates(
ata.address,
newCertificateVaultAta.address
);
const certificateVaultInfo = await getAccount(
connection,
newCertificateVault.address
newCertificateVaultAta.address
);

expect(Number(certificateVaultInfo.amount)).to.equal(mintAmount);
Expand Down
20 changes: 10 additions & 10 deletions packages/types/fund_sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export type FundSender = {
]
},
{
"name": "certificateVault",
"name": "certificateVaultAta",
"isMut": true,
"isSigner": false
},
Expand Down Expand Up @@ -228,13 +228,13 @@ export type FundSender = {
},
{
"code": 6003,
"name": "IncorrectDestinationAccount",
"msg": "Incorrect destination account"
"name": "MintMismatch",
"msg": "Source and destination token accounts do not have the same mint"
},
{
"code": 6004,
"name": "IncorrectHoldAccount",
"msg": "Incorrect hold account"
"name": "IncorrectDestinationAccount",
"msg": "Incorrect destination account"
},
{
"code": 6005,
Expand Down Expand Up @@ -371,7 +371,7 @@ export const IDL: FundSender = {
]
},
{
"name": "certificateVault",
"name": "certificateVaultAta",
"isMut": true,
"isSigner": false
},
Expand Down Expand Up @@ -474,13 +474,13 @@ export const IDL: FundSender = {
},
{
"code": 6003,
"name": "IncorrectDestinationAccount",
"msg": "Incorrect destination account"
"name": "MintMismatch",
"msg": "Source and destination token accounts do not have the same mint"
},
{
"code": 6004,
"name": "IncorrectHoldAccount",
"msg": "Incorrect hold account"
"name": "IncorrectDestinationAccount",
"msg": "Incorrect destination account"
},
{
"code": 6005,
Expand Down
4 changes: 2 additions & 2 deletions programs/fund-sender/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ pub mod fund_sender {
let state = &mut ctx.accounts.state;
let output_yield_account = &mut ctx.accounts.output_yield_account;
let output_yield_token_account = &mut ctx.accounts.output_yield_token_account;
let certificate_vault = &mut ctx.accounts.certificate_vault;
let certificate_vault_ata = &mut ctx.accounts.certificate_vault_ata;

let amount: u64 = output_yield_token_account.amount;
transfer_token(
&state.key(),
&AccountsTokenTransfer {
source: output_yield_token_account.to_account_info(),
dest: certificate_vault.to_account_info(),
dest: certificate_vault_ata.to_account_info(),
authority: output_yield_account.to_account_info(),
},
amount,
Expand Down
6 changes: 3 additions & 3 deletions programs/fund-sender/src/utils/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ pub enum ErrorCode {
#[msg("Token account not owned by output yield account")]
IncorrectTokenAccountOwner,

#[msg("Source and destination token accounts do not have the same mint")]
MintMismatch,

#[msg("Incorrect destination account")]
IncorrectDestinationAccount,

#[msg("Incorrect hold account")]
IncorrectHoldAccount,

#[msg("Incorrect update authority")]
Unauthorized,
}
6 changes: 4 additions & 2 deletions programs/fund-sender/src/utils/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,11 @@ pub struct StoreCertificates<'info> {
pub output_yield_token_account: Account<'info, TokenAccount>,
#[account(
mut,
constraint = certificate_vault.key() == state.certificate_vault @ ErrorCode::IncorrectHoldAccount,
associated_token::mint = output_yield_token_account.mint,
associated_token::authority = state.certificate_vault,
)]
// the account where we store all the certificates
pub certificate_vault: Account<'info, TokenAccount>,
pub certificate_vault_ata: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
// pub system_program: Program<'info, System>,
}

0 comments on commit e1e290a

Please sign in to comment.