Skip to content

Commit 66caed6

Browse files
committed
fix: move refund_address to accounts
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
1 parent 17222a0 commit 66caed6

File tree

3 files changed

+28
-29
lines changed

3 files changed

+28
-29
lines changed

programs/svm-spoke/src/instructions/refund_claims.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ pub fn initialize_claim_account(ctx: Context<InitializeClaimAccount>) -> Result<
3636

3737
#[event_cpi]
3838
#[derive(Accounts)]
39-
#[instruction(refund_address: Option<Pubkey>)]
4039
pub struct ClaimRelayerRefund<'info> {
4140
pub signer: Signer<'info>,
4241

@@ -59,30 +58,33 @@ pub struct ClaimRelayerRefund<'info> {
5958
#[account(mint::token_program = token_program)]
6059
pub mint: InterfaceAccount<'info, Mint>,
6160

62-
// If refund_address is not provided this method allows relayer to claim refunds on any custom token account.
61+
/// CHECK: This is used for claim_account PDA derivation and it is up to the caller to ensure it is valid.
62+
pub refund_address: UncheckedAccount<'info>,
63+
64+
// If refund_address is the same as signer this method allows relayer to claim refunds on any custom token account.
6365
// Otherwise this must be the associated token account of the provided refund_address.
6466
#[account(
6567
mut,
6668
token::mint = mint,
6769
token::token_program = token_program,
68-
constraint = refund_address.is_none()
69-
|| is_valid_associated_token_account(&token_account, &mint, &token_program, &refund_address.unwrap())
70+
constraint = refund_address.key().eq(&signer.key())
71+
|| is_valid_associated_token_account(&token_account, &mint, &token_program, &refund_address.key())
7072
@ SvmError::InvalidRefundTokenAccount
7173
)]
7274
pub token_account: InterfaceAccount<'info, TokenAccount>,
7375

7476
#[account(
7577
mut,
7678
close = initializer,
77-
seeds = [b"claim_account", mint.key().as_ref(), refund_address.unwrap_or_else(|| signer.key()).as_ref()],
79+
seeds = [b"claim_account", mint.key().as_ref(), refund_address.key().as_ref()],
7880
bump
7981
)]
8082
pub claim_account: Account<'info, ClaimAccount>,
8183

8284
pub token_program: Interface<'info, TokenInterface>,
8385
}
8486

85-
pub fn claim_relayer_refund(ctx: Context<ClaimRelayerRefund>, refund_address: Option<Pubkey>) -> Result<()> {
87+
pub fn claim_relayer_refund(ctx: Context<ClaimRelayerRefund>) -> Result<()> {
8688
// Ensure the claim account holds a non-zero amount.
8789
let claim_amount = ctx.accounts.claim_account.amount;
8890
if claim_amount == 0 {
@@ -108,7 +110,7 @@ pub fn claim_relayer_refund(ctx: Context<ClaimRelayerRefund>, refund_address: Op
108110
emit_cpi!(ClaimedRelayerRefund {
109111
l2_token_address: ctx.accounts.mint.key(),
110112
claim_amount,
111-
refund_address: refund_address.unwrap_or_else(|| ctx.accounts.signer.key()),
113+
refund_address: ctx.accounts.refund_address.key(),
112114
});
113115

114116
Ok(()) // There is no need to reset the claim amount as the account will be closed at the end of instruction.

programs/svm-spoke/src/lib.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -455,15 +455,13 @@ pub mod svm_spoke {
455455
/// - state (Account): Spoke state PDA. Seed: ["state",state.seed] where seed is 0 on mainnet.
456456
/// - vault (InterfaceAccount): The ATA for the refunded mint. Authority must be the state.
457457
/// - mint (InterfaceAccount): The mint account for the token being refunded.
458-
/// - token_account (InterfaceAccount): The receiving token account for the refund. When refund_address is provided,
459-
/// this must match its ATA.
458+
/// - refund_address: token account authority receiving the refund.
459+
/// - token_account (InterfaceAccount): The receiving token account for the refund. When refund_address is different
460+
/// from the signer, this must match its ATA.
460461
/// - claim_account (Account): The claim account PDA. Seed: ["claim_account",mint,refund_address].
461462
/// - token_program (Interface): The token program.
462-
///
463-
/// ### Parameters:
464-
/// - refund_address: Optional token account authority receiving the refund. If None, the signer is used.
465-
pub fn claim_relayer_refund(ctx: Context<ClaimRelayerRefund>, refund_address: Option<Pubkey>) -> Result<()> {
466-
instructions::claim_relayer_refund(ctx, refund_address)
463+
pub fn claim_relayer_refund(ctx: Context<ClaimRelayerRefund>) -> Result<()> {
464+
instructions::claim_relayer_refund(ctx)
467465
}
468466

469467
/// Creates token accounts in batch for a set of addresses.

test/svm/SvmSpoke.RefundClaims.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ describe("svm_spoke.refund_claims", () => {
3838
state: PublicKey;
3939
vault: PublicKey;
4040
mint: PublicKey;
41+
refundAddress: PublicKey;
4142
tokenAccount: PublicKey;
4243
claimAccount: PublicKey;
4344
tokenProgram: PublicKey;
@@ -149,6 +150,7 @@ describe("svm_spoke.refund_claims", () => {
149150
state,
150151
vault,
151152
mint,
153+
refundAddress: relayer.publicKey,
152154
tokenAccount,
153155
claimAccount,
154156
tokenProgram: TOKEN_PROGRAM_ID,
@@ -176,7 +178,7 @@ describe("svm_spoke.refund_claims", () => {
176178
const iRelayerBal = (await connection.getTokenAccountBalance(tokenAccount)).value.amount;
177179

178180
// Claim refund for the relayer.
179-
const tx = await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
181+
const tx = await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
180182

181183
// The relayer should have received funds from the vault.
182184
const fVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount;
@@ -198,11 +200,11 @@ describe("svm_spoke.refund_claims", () => {
198200
await executeRelayerRefundToClaim(relayerRefund);
199201

200202
// Claim refund for the relayer.
201-
await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
203+
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
202204

203205
// The claim account should have been automatically closed, so repeated claim should fail.
204206
try {
205-
await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
207+
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
206208
assert.fail("Claiming refund from closed account should fail");
207209
} catch (error: any) {
208210
assert.instanceOf(error, AnchorError);
@@ -216,7 +218,7 @@ describe("svm_spoke.refund_claims", () => {
216218
// After reinitalizing the claim account, the repeated claim should still fail.
217219
await initializeClaimAccount();
218220
try {
219-
await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
221+
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
220222
assert.fail("Claiming refund from reinitalized account should fail");
221223
} catch (error: any) {
222224
assert.instanceOf(error, AnchorError);
@@ -235,7 +237,7 @@ describe("svm_spoke.refund_claims", () => {
235237
const iRelayerBal = (await connection.getTokenAccountBalance(tokenAccount)).value.amount;
236238

237239
// Claim refund for the relayer.
238-
await await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
240+
await await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
239241

240242
// The relayer should have received both refunds.
241243
const fVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount;
@@ -260,7 +262,7 @@ describe("svm_spoke.refund_claims", () => {
260262

261263
// Claiming with default initializer should fail.
262264
try {
263-
await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
265+
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
264266
} catch (error: any) {
265267
assert.instanceOf(error, AnchorError);
266268
assert.strictEqual(
@@ -272,7 +274,7 @@ describe("svm_spoke.refund_claims", () => {
272274

273275
// Claim refund for the relayer passing the correct initializer account.
274276
claimRelayerRefundAccounts.initializer = anotherInitializer.publicKey;
275-
await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
277+
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
276278

277279
// The relayer should have received funds from the vault.
278280
const fVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount;
@@ -344,7 +346,7 @@ describe("svm_spoke.refund_claims", () => {
344346
claimRelayerRefundAccounts.tokenAccount = wrongTokenAccount;
345347

346348
try {
347-
await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
349+
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
348350
assert.fail("Claiming refund to custom token account should fail");
349351
} catch (error: any) {
350352
assert.instanceOf(error, AnchorError);
@@ -369,7 +371,7 @@ describe("svm_spoke.refund_claims", () => {
369371
await setAuthority(connection, payer, wrongTokenAccount, wrongOwner, AuthorityType.AccountOwner, relayer.publicKey);
370372

371373
try {
372-
await program.methods.claimRelayerRefund(relayer.publicKey).accounts(claimRelayerRefundAccounts).rpc();
374+
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
373375
assert.fail("Claiming refund to custom token account should fail");
374376
} catch (error: any) {
375377
assert.instanceOf(error, AnchorError);
@@ -396,11 +398,7 @@ describe("svm_spoke.refund_claims", () => {
396398
claimRelayerRefundAccounts.signer = relayer.publicKey; // Only relayer itself should be able to do this.
397399

398400
// Relayer can claim refund to custom token account.
399-
const tx = await program.methods
400-
.claimRelayerRefund(null)
401-
.accounts(claimRelayerRefundAccounts)
402-
.signers([relayer])
403-
.rpc();
401+
const tx = await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).signers([relayer]).rpc();
404402

405403
// The relayer should have received funds from the vault.
406404
const fVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount;
@@ -422,8 +420,9 @@ describe("svm_spoke.refund_claims", () => {
422420
await executeRelayerRefundToClaim(relayerRefund);
423421

424422
// Claim refund for the relayer with the default signer should fail as relayer address is part of claim account derivation.
423+
claimRelayerRefundAccounts.refundAddress = owner;
425424
try {
426-
await program.methods.claimRelayerRefund(null).accounts(claimRelayerRefundAccounts).rpc();
425+
await program.methods.claimRelayerRefund().accounts(claimRelayerRefundAccounts).rpc();
427426
assert.fail("Claiming refund with wrong signer should fail");
428427
} catch (error: any) {
429428
assert.instanceOf(error, AnchorError);

0 commit comments

Comments
 (0)