Skip to content

Commit

Permalink
fix: origin token constraint in set enable route
Browse files Browse the repository at this point in the history
Signed-off-by: Reinis Martinsons <[email protected]>
  • Loading branch information
Reinis-FRP committed Sep 20, 2024
1 parent 12aeb55 commit 9d82215
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 23 deletions.
6 changes: 5 additions & 1 deletion programs/svm-spoke/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,11 @@ pub struct SetEnableRoute<'info> {
)]
pub vault: InterfaceAccount<'info, TokenAccount>,

#[account(mint::token_program = token_program)]
#[account(
mint::token_program = token_program,
// IDL build fails when requiring `address = input_token` for mint, thus using a custom constraint.
constraint = origin_token_mint.key() == origin_token.into() @ CustomError::InvalidMint
)]
pub origin_token_mint: InterfaceAccount<'info, Mint>,

pub token_program: Interface<'info, TokenInterface>,
Expand Down
7 changes: 3 additions & 4 deletions test/svm/SvmSpoke.HandleReceiveMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ describe("svm_spoke.handle_receive_message", () => {

it("Enables and disables route remotely", async () => {
// Enable the route.
const originToken = Keypair.generate().publicKey;
const originToken = await createMint(provider.connection, provider.wallet.payer, owner, owner, 6);
const routeChainId = 1;
let calldata = ethereumIface.encodeFunctionData("setEnableRoute", [originToken.toBuffer(), routeChainId, true]);
let messageBody = Buffer.from(calldata.slice(2), "hex");
Expand All @@ -327,8 +327,7 @@ describe("svm_spoke.handle_receive_message", () => {

// Remaining accounts specific to SetEnableRoute.
const routePda = createRoutePda(originToken, new anchor.BN(routeChainId));
const tokenMint = await createMint(provider.connection, provider.wallet.payer, owner, owner, 6);
const vault = getVaultAta(tokenMint, state);
const vault = getVaultAta(originToken, state);
// Same 3 remaining accounts passed for HandleReceiveMessage context.
const enableRouteRemainingAccounts = remainingAccounts.slice(0, 3);
// payer in self-invoked SetEnableRoute.
Expand Down Expand Up @@ -359,7 +358,7 @@ describe("svm_spoke.handle_receive_message", () => {
enableRouteRemainingAccounts.push({
isSigner: false,
isWritable: false,
pubkey: tokenMint,
pubkey: originToken,
});
// token_program in self-invoked SetEnableRoute.
enableRouteRemainingAccounts.push({
Expand Down
57 changes: 39 additions & 18 deletions test/svm/SvmSpoke.Routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,25 @@ describe("svm_spoke.routes", () => {
anchor.setProvider(provider);

const nonOwner = Keypair.generate();
let state: PublicKey, tokenMint: PublicKey;
let state: PublicKey, tokenMint: PublicKey, routePda: PublicKey, vault: PublicKey;
let routeChainId: BN;
let setEnableRouteAccounts: any;

before("Creates token mint and associated token accounts", async () => {
tokenMint = await createMint(provider.connection, provider.wallet.payer, owner, owner, 6);
});
before("Creates token mint and associated token accounts", async () => {});

beforeEach(async () => {
state = await initializeState();
});

it("Sets, retrieves, and controls access to route enablement", async () => {
const originToken = Keypair.generate().publicKey;
const routeChainId = new BN(1);
tokenMint = await createMint(provider.connection, provider.wallet.payer, owner, owner, 6);

// Create a PDA for the route
const routePda = createRoutePda(originToken, routeChainId);
routeChainId = new BN(1);
routePda = createRoutePda(tokenMint, routeChainId);

// Create ATA for the origin token to be stored by state (vault).
const vault = getVaultAta(tokenMint, state);
vault = getVaultAta(tokenMint, state);

// Common accounts object
const accounts = {
setEnableRouteAccounts = {
signer: owner,
payer: owner,
state,
Expand All @@ -44,9 +41,14 @@ describe("svm_spoke.routes", () => {
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
systemProgram: anchor.web3.SystemProgram.programId,
};
});

it("Sets, retrieves, and controls access to route enablement", async () => {
// Enable the route as owner
await program.methods.setEnableRoute(originToken.toBytes(), routeChainId, true).accounts(accounts).rpc();
await program.methods
.setEnableRoute(tokenMint.toBytes(), routeChainId, true)
.accounts(setEnableRouteAccounts)
.rpc();
await new Promise((resolve) => setTimeout(resolve, 500));

// Retrieve and verify the route is enabled
Expand All @@ -58,12 +60,15 @@ describe("svm_spoke.routes", () => {
(event) => event.name === "enabledDepositRoute"
);
let event = events[0].data;
assert.strictEqual(event.originToken.toString(), originToken.toString(), "originToken event match");
assert.strictEqual(event.originToken.toString(), tokenMint.toString(), "originToken event match");
assert.strictEqual(event.destinationChainId.toString(), routeChainId.toString(), "destinationChainId should match");
assert.isTrue(event.enabled, "enabledDepositRoute enabled");

// Disable the route as owner
await program.methods.setEnableRoute(originToken.toBytes(), routeChainId, false).accounts(accounts).rpc();
await program.methods
.setEnableRoute(tokenMint.toBytes(), routeChainId, false)
.accounts(setEnableRouteAccounts)
.rpc();
await new Promise((resolve) => setTimeout(resolve, 500));

// Retrieve and verify the route is disabled
Expand All @@ -75,15 +80,15 @@ describe("svm_spoke.routes", () => {
(event) => event.name === "enabledDepositRoute"
);
event = events[0].data; // take most recent event, index 0.
assert.strictEqual(event.originToken.toString(), originToken.toString(), "originToken event match");
assert.strictEqual(event.originToken.toString(), tokenMint.toString(), "originToken event match");
assert.strictEqual(event.destinationChainId.toString(), routeChainId.toString(), "destinationChainId should match");
assert.isFalse(event.enabled, "enabledDepositRoute disabled");

// Try to enable the route as non-owner
try {
await program.methods
.setEnableRoute(originToken.toBytes(), routeChainId, true)
.accounts({ ...accounts, signer: nonOwner.publicKey })
.setEnableRoute(tokenMint.toBytes(), routeChainId, true)
.accounts({ ...setEnableRouteAccounts, signer: nonOwner.publicKey })
.signers([nonOwner])
.rpc();
assert.fail("Non-owner should not be able to set route enablement");
Expand All @@ -103,4 +108,20 @@ describe("svm_spoke.routes", () => {
const stateAccount = await program.account.state.fetch(state);
assert.strictEqual(stateAccount.owner.toBase58(), owner.toBase58(), "State owner should be the expected owner");
});

it("Cannot misconfigure route with wrong origin token", async () => {
const wrongOriginToken = Keypair.generate().publicKey;
const wrongRoutePda = createRoutePda(wrongOriginToken, routeChainId);

try {
await program.methods
.setEnableRoute(wrongOriginToken.toBytes(), routeChainId, true)
.accounts({ ...setEnableRouteAccounts, route: wrongRoutePda })
.rpc();
assert.fail("Setting route with wrong origin token should fail");
} catch (err) {
assert.instanceOf(err, anchor.AnchorError);
assert.strictEqual(err.error.errorCode.code, "InvalidMint", "Expected error code InvalidMint");
}
});
});

0 comments on commit 9d82215

Please sign in to comment.