Skip to content

Commit 1c4ad22

Browse files
authored
feat(svm): fill with across plus and codama test (#985)
* feat(svm): fill with across plus and codama test Signed-off-by: Pablo Maldonado <[email protected]> * fix: remove .only from tests Signed-off-by: Pablo Maldonado <[email protected]> --------- Signed-off-by: Pablo Maldonado <[email protected]>
1 parent 1712840 commit 1c4ad22

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed

test/svm/SvmSpoke.Fill.AcrossPlus.ts

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ import {
3333
import { MulticallHandler } from "../../target/types/multicall_handler";
3434
import { common } from "./SvmSpoke.common";
3535
import { FillDataParams, FillDataValues } from "../../src/types/svm";
36+
import { getApproveCheckedInstruction, getTransferCheckedInstruction } from "@solana-program/token";
37+
import {
38+
AccountRole,
39+
address,
40+
appendTransactionMessageInstruction,
41+
createKeyPairFromBytes,
42+
createSignerFromKeyPair,
43+
getProgramDerivedAddress,
44+
IAccountMeta,
45+
pipe,
46+
} from "@solana/kit";
47+
import { createDefaultSolanaClient, createDefaultTransaction, signAndSendTransaction } from "./utils";
48+
import { FillRelayAsyncInput } from "../../src/svm/clients/SvmSpoke";
49+
import { SvmSpokeClient } from "../../src/svm";
3650
const { provider, connection, program, owner, chainId, seedBalance } = common;
3751
const { initializeState, assertSE } = common;
3852

@@ -384,4 +398,141 @@ describe("svm_spoke.fill.across_plus", () => {
384398
"Recipient's balance should be increased by the relay amount"
385399
);
386400
});
401+
402+
describe("codama client and solana kit", () => {
403+
it("Forwards tokens to the final recipient within invoked message call using codama client", async () => {
404+
const iRelayerBal = (await getAccount(connection, relayerATA)).amount;
405+
406+
// Construct ix to transfer all tokens from handler to the final recipient.
407+
const transferIx = createTransferCheckedInstruction(
408+
handlerATA,
409+
mint,
410+
finalRecipientATA,
411+
handlerSigner,
412+
relayData.outputAmount,
413+
mintDecimals
414+
);
415+
416+
const multicallHandlerCoder = new MulticallHandlerCoder([transferIx]);
417+
418+
const handlerMessage = multicallHandlerCoder.encode();
419+
420+
const message = new AcrossPlusMessageCoder({
421+
handler: handlerProgram.programId,
422+
readOnlyLen: multicallHandlerCoder.readOnlyLen,
423+
valueAmount: new BN(0),
424+
accounts: multicallHandlerCoder.compiledMessage.accountKeys,
425+
handlerMessage,
426+
});
427+
428+
const encodedMessage = message.encode();
429+
430+
// Update relay data with the encoded message.
431+
const newRelayData = { ...relayData, message: encodedMessage };
432+
updateRelayData(newRelayData);
433+
434+
const rpcClient = createDefaultSolanaClient();
435+
const signer = await createSignerFromKeyPair(await createKeyPairFromBytes(relayer.secretKey));
436+
437+
const [eventAuthority] = await getProgramDerivedAddress({
438+
programAddress: address(program.programId.toString()),
439+
seeds: ["__event_authority"],
440+
});
441+
const relayHash = Array.from(calculateRelayHashUint8Array(relayData, chainId));
442+
443+
const formattedAccounts = {
444+
state: address(accounts.state.toString()),
445+
instructionParams: address(program.programId.toString()),
446+
mint: address(mint.toString()),
447+
relayerTokenAccount: address(relayerATA.toString()),
448+
recipientTokenAccount: address(handlerATA.toString()),
449+
fillStatus: address(accounts.fillStatus.toString()),
450+
tokenProgram: address(TOKEN_PROGRAM_ID.toString()),
451+
associatedTokenProgram: address(ASSOCIATED_TOKEN_PROGRAM_ID.toString()),
452+
systemProgram: address(anchor.web3.SystemProgram.programId.toString()),
453+
program: address(program.programId.toString()),
454+
eventAuthority,
455+
signer,
456+
};
457+
458+
const formattedRelayData = {
459+
relayHash: new Uint8Array(relayHash),
460+
relayData: {
461+
depositor: address(relayData.depositor.toString()),
462+
recipient: address(relayData.recipient.toString()),
463+
exclusiveRelayer: address(relayData.exclusiveRelayer.toString()),
464+
inputToken: address(relayData.inputToken.toString()),
465+
outputToken: address(relayData.outputToken.toString()),
466+
inputAmount: relayData.inputAmount.toNumber(),
467+
outputAmount: relayData.outputAmount.toNumber(),
468+
originChainId: relayData.originChainId.toNumber(),
469+
depositId: new Uint8Array(relayData.depositId),
470+
fillDeadline: relayData.fillDeadline,
471+
exclusivityDeadline: relayData.exclusivityDeadline,
472+
message: encodedMessage,
473+
},
474+
repaymentChainId: 1,
475+
repaymentAddress: address(relayer.publicKey.toString()),
476+
};
477+
478+
const approveIx = getApproveCheckedInstruction({
479+
source: address(accounts.relayerTokenAccount.toString()),
480+
mint: address(accounts.mint.toString()),
481+
delegate: address(accounts.state.toString()),
482+
owner: address(accounts.signer.toString()),
483+
amount: BigInt(relayData.outputAmount.toString()),
484+
decimals: mintDecimals,
485+
});
486+
487+
const fillRelayInput: FillRelayAsyncInput = {
488+
...formattedRelayData,
489+
...formattedAccounts,
490+
};
491+
492+
const fillRelayIxData = await SvmSpokeClient.getFillRelayInstructionAsync(fillRelayInput);
493+
const fillRelayIx = {
494+
...fillRelayIxData,
495+
accounts: fillRelayIxData.accounts.map((account) =>
496+
account.address === program.programId.toString() ||
497+
account.address === TOKEN_PROGRAM_ID.toString() ||
498+
account.address === ASSOCIATED_TOKEN_PROGRAM_ID.toString()
499+
? { ...account, role: AccountRole.READONLY }
500+
: account
501+
),
502+
};
503+
504+
const _remainingAccounts: AccountMeta[] = [
505+
{ pubkey: handlerProgram.programId, isSigner: false, isWritable: false },
506+
...multicallHandlerCoder.compiledKeyMetas,
507+
];
508+
const remainingAccounts: IAccountMeta<string>[] = _remainingAccounts.map((account) => ({
509+
address: address(account.pubkey.toString()),
510+
role: account.isWritable ? AccountRole.WRITABLE : AccountRole.READONLY,
511+
}));
512+
(fillRelayIx.accounts as IAccountMeta<string>[]).push(...remainingAccounts);
513+
514+
await pipe(
515+
await createDefaultTransaction(rpcClient, signer),
516+
(tx) => appendTransactionMessageInstruction(approveIx, tx),
517+
(tx) => appendTransactionMessageInstruction(fillRelayIx, tx),
518+
(tx) => signAndSendTransaction(rpcClient, tx)
519+
);
520+
521+
// Verify relayer's balance after the fill
522+
const fRelayerBal = (await getAccount(connection, relayerATA)).amount;
523+
assertSE(
524+
fRelayerBal,
525+
iRelayerBal - BigInt(relayAmount),
526+
"Relayer's balance should be reduced by the relay amount"
527+
);
528+
529+
// Verify final recipient's balance after the fill
530+
const finalRecipientAccount = await getAccount(connection, finalRecipientATA);
531+
assertSE(
532+
finalRecipientAccount.amount,
533+
relayAmount,
534+
"Final recipient's balance should be increased by the relay amount"
535+
);
536+
});
537+
});
387538
});

0 commit comments

Comments
 (0)