Skip to content

Commit 9fa19fd

Browse files
authored
feat(svm): fill tests with codama and solana kit (#922)
* feat(svm): fill tests with codama and solana kit Signed-off-by: Pablo Maldonado <[email protected]> * fix: remove extra prop Signed-off-by: Pablo Maldonado <[email protected]> * fix: cleanup Signed-off-by: Pablo Maldonado <[email protected]> --------- Signed-off-by: Pablo Maldonado <[email protected]>
1 parent 370a9b0 commit 9fa19fd

File tree

2 files changed

+135
-14
lines changed

2 files changed

+135
-14
lines changed

test/svm/SvmSpoke.Fill.ts

Lines changed: 134 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,49 @@
11
import * as anchor from "@coral-xyz/anchor";
22
import { BN, web3 } from "@coral-xyz/anchor";
3+
import { getApproveCheckedInstruction } from "@solana-program/token";
4+
import {
5+
AccountRole,
6+
address,
7+
appendTransactionMessageInstruction,
8+
createKeyPairFromBytes,
9+
createSignerFromKeyPair,
10+
getProgramDerivedAddress,
11+
IAccountMeta,
12+
pipe,
13+
} from "@solana/kit";
314
import {
415
ASSOCIATED_TOKEN_PROGRAM_ID,
5-
TOKEN_PROGRAM_ID,
6-
TOKEN_2022_PROGRAM_ID,
716
createAccount,
8-
createMint,
9-
getOrCreateAssociatedTokenAccount,
10-
mintTo,
11-
getAccount,
12-
getAssociatedTokenAddressSync,
1317
createApproveCheckedInstruction,
14-
createReallocateInstruction,
1518
createEnableCpiGuardInstruction,
19+
createMint,
20+
createReallocateInstruction,
1621
ExtensionType,
22+
getAccount,
23+
getAssociatedTokenAddressSync,
24+
getOrCreateAssociatedTokenAccount,
25+
mintTo,
26+
TOKEN_2022_PROGRAM_ID,
27+
TOKEN_PROGRAM_ID,
1728
} from "@solana/spl-token";
18-
import { PublicKey, Keypair, TransactionInstruction, sendAndConfirmTransaction, Transaction } from "@solana/web3.js";
29+
import { Keypair, PublicKey, sendAndConfirmTransaction, Transaction, TransactionInstruction } from "@solana/web3.js";
30+
import { SvmSpokeClient } from "../../src/svm";
31+
import { FillRelayAsyncInput } from "../../src/svm/clients/SvmSpoke";
1932
import {
20-
readEventsUntilFound,
2133
calculateRelayHashUint8Array,
22-
sendTransactionWithLookupTable,
2334
hashNonEmptyMessage,
2435
intToU8Array32,
36+
readEventsUntilFound,
37+
sendTransactionWithLookupTable,
2538
} from "../../src/svm/web3-v1";
26-
import { common } from "./SvmSpoke.common";
27-
import { testAcrossPlusMessage } from "./utils";
2839
import { FillDataValues, RelayData } from "../../src/types/svm";
40+
import { common } from "./SvmSpoke.common";
41+
import {
42+
createDefaultSolanaClient,
43+
createDefaultTransaction,
44+
signAndSendTransaction,
45+
testAcrossPlusMessage,
46+
} from "./utils";
2947
const { provider, connection, program, owner, chainId, seedBalance } = common;
3048
const { recipient, initializeState, setCurrentTime, assertSE, assert } = common;
3149

@@ -631,4 +649,107 @@ describe("svm_spoke.fill", () => {
631649
assertSE(event.messageHash, new Uint8Array(32), `MessageHash should be zeroed`);
632650
assertSE(event.relayExecutionInfo.updatedMessageHash, new Uint8Array(32), `UpdatedMessageHash should be zeroed`);
633651
});
652+
653+
describe("codama client and solana kit", () => {
654+
it("Fills a V3 relay and verifies balances with codama client and solana kit", async () => {
655+
const rpcClient = createDefaultSolanaClient();
656+
const signer = await createSignerFromKeyPair(await createKeyPairFromBytes(relayer.secretKey));
657+
658+
const [eventAuthority] = await getProgramDerivedAddress({
659+
programAddress: address(program.programId.toString()),
660+
seeds: ["__event_authority"],
661+
});
662+
663+
let recipientAccount = await getAccount(connection, recipientTA);
664+
assertSE(recipientAccount.amount, "0", "Recipient's balance should be 0 before the fill");
665+
666+
let relayerAccount = await getAccount(connection, relayerTA);
667+
assertSE(relayerAccount.amount, seedBalance, "Relayer's balance should be equal to seed balance before the fill");
668+
669+
const relayHash = Array.from(calculateRelayHashUint8Array(relayData, chainId));
670+
671+
const formattedAccounts = {
672+
state: address(accounts.state.toString()),
673+
instructionParams: address(program.programId.toString()),
674+
mint: address(mint.toString()),
675+
relayerTokenAccount: address(relayerTA.toString()),
676+
recipientTokenAccount: address(recipientTA.toString()),
677+
fillStatus: address(accounts.fillStatus.toString()),
678+
tokenProgram: address(TOKEN_PROGRAM_ID.toString()),
679+
associatedTokenProgram: address(ASSOCIATED_TOKEN_PROGRAM_ID.toString()),
680+
systemProgram: address(anchor.web3.SystemProgram.programId.toString()),
681+
program: address(program.programId.toString()),
682+
eventAuthority,
683+
signer,
684+
};
685+
686+
const formattedRelayData = {
687+
relayHash: new Uint8Array(relayHash),
688+
relayData: {
689+
depositor: address(relayData.depositor.toString()),
690+
recipient: address(relayData.recipient.toString()),
691+
exclusiveRelayer: address(relayData.exclusiveRelayer.toString()),
692+
inputToken: address(relayData.inputToken.toString()),
693+
outputToken: address(relayData.outputToken.toString()),
694+
inputAmount: relayData.inputAmount.toNumber(),
695+
outputAmount: relayData.outputAmount.toNumber(),
696+
originChainId: relayData.originChainId.toNumber(),
697+
depositId: new Uint8Array(relayData.depositId),
698+
fillDeadline: relayData.fillDeadline,
699+
exclusivityDeadline: relayData.exclusivityDeadline,
700+
message: relayData.message,
701+
},
702+
repaymentChainId: 1,
703+
repaymentAddress: address(relayer.publicKey.toString()),
704+
};
705+
706+
const approveIx = getApproveCheckedInstruction({
707+
source: address(accounts.relayerTokenAccount.toString()),
708+
mint: address(accounts.mint.toString()),
709+
delegate: address(accounts.state.toString()),
710+
owner: address(accounts.signer.toString()),
711+
amount: BigInt(relayData.outputAmount.toString()),
712+
decimals: tokenDecimals,
713+
});
714+
715+
const fillRelayInput: FillRelayAsyncInput = {
716+
...formattedRelayData,
717+
...formattedAccounts,
718+
};
719+
720+
const fillRelayIxData = await SvmSpokeClient.getFillRelayInstructionAsync(fillRelayInput);
721+
const fillRelayIx = {
722+
...fillRelayIxData,
723+
accounts: fillRelayIxData.accounts.map((account) =>
724+
account.address === program.programId.toString() ? { ...account, role: AccountRole.READONLY } : account
725+
),
726+
};
727+
const remainingAccounts: IAccountMeta<string>[] = fillRemainingAccounts.map((account) => ({
728+
address: address(account.pubkey.toString()),
729+
role: AccountRole.WRITABLE,
730+
}));
731+
(fillRelayIx.accounts as IAccountMeta<string>[]).push(...remainingAccounts);
732+
733+
const tx = await pipe(
734+
await createDefaultTransaction(rpcClient, signer),
735+
(tx) => appendTransactionMessageInstruction(approveIx, tx),
736+
(tx) => appendTransactionMessageInstruction(fillRelayIx, tx),
737+
(tx) => signAndSendTransaction(rpcClient, tx)
738+
);
739+
740+
const events = await readEventsUntilFound(connection, tx, [program]);
741+
const event = events.find((event) => event.name === "filledRelay")?.data;
742+
assert.isNotNull(event, "FilledRelay event should be emitted");
743+
744+
relayerAccount = await getAccount(connection, relayerTA);
745+
assertSE(
746+
relayerAccount.amount,
747+
seedBalance - relayAmount,
748+
"Relayer's balance should be reduced by the relay amount"
749+
);
750+
751+
recipientAccount = await getAccount(connection, recipientTA);
752+
assertSE(recipientAccount.amount, relayAmount, "Recipient's balance should be increased by the relay amount");
753+
});
754+
});
634755
});

test/svm/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ export const createDefaultSolanaClient = () => {
197197
return { rpc, rpcSubscriptions };
198198
};
199199

200-
type RpcClient = {
200+
export type RpcClient = {
201201
rpc: Rpc<SolanaRpcApiFromTransport<RpcTransport>>;
202202
rpcSubscriptions: RpcSubscriptions<SignatureNotificationsApi & SlotNotificationsApi>;
203203
};

0 commit comments

Comments
 (0)