Skip to content

Commit 02751fe

Browse files
author
0xOpenClaw
committed
fix(transfer-sol): require payer signature + checked lamport math
1 parent 4babc04 commit 02751fe

File tree

2 files changed

+31
-4
lines changed
  • basics/transfer-sol/anchor

2 files changed

+31
-4
lines changed

basics/transfer-sol/anchor/programs/transfer-sol/src/lib.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,36 @@ pub mod transfer_sol {
2727
ctx: Context<TransferSolWithProgram>,
2828
amount: u64,
2929
) -> Result<()> {
30-
**ctx.accounts.payer.try_borrow_mut_lamports()? -= amount;
31-
**ctx.accounts.recipient.try_borrow_mut_lamports()? += amount;
30+
// Security invariants:
31+
// - The source account must authorize the transfer (is_signer)
32+
// - The source account must be owned by this program (direct lamports mutation)
33+
// - Prevent under/overflow on lamport arithmetic
34+
35+
let payer_lamports = ctx.accounts.payer.to_account_info().lamports();
36+
require!(payer_lamports >= amount, TransferSolError::InsufficientFunds);
37+
38+
**ctx.accounts.payer.try_borrow_mut_lamports()? = payer_lamports
39+
.checked_sub(amount)
40+
.ok_or(TransferSolError::LamportArithmeticOverflow)?;
41+
42+
let recipient_lamports = ctx.accounts.recipient.to_account_info().lamports();
43+
**ctx.accounts.recipient.try_borrow_mut_lamports()? = recipient_lamports
44+
.checked_add(amount)
45+
.ok_or(TransferSolError::LamportArithmeticOverflow)?;
46+
3247
Ok(())
3348
}
3449
}
3550

51+
#[error_code]
52+
pub enum TransferSolError {
53+
#[msg("Insufficient funds in payer account")]
54+
InsufficientFunds,
55+
56+
#[msg("Lamport arithmetic overflow/underflow")]
57+
LamportArithmeticOverflow,
58+
}
59+
3660
#[derive(Accounts)]
3761
pub struct TransferSolWithCpi<'info> {
3862
#[account(mut)]
@@ -44,12 +68,14 @@ pub struct TransferSolWithCpi<'info> {
4468

4569
#[derive(Accounts)]
4670
pub struct TransferSolWithProgram<'info> {
47-
/// CHECK: Use owner constraint to check account is owned by our program
71+
// NOTE: This account must sign the transaction, otherwise *anyone* could drain lamports
72+
// from program-owned accounts passed into this instruction.
4873
#[account(
4974
mut,
5075
owner = id() // value of declare_id!()
5176
)]
52-
payer: UncheckedAccount<'info>,
77+
payer: Signer<'info>,
78+
5379
#[account(mut)]
5480
recipient: SystemAccount<'info>,
5581
}

basics/transfer-sol/anchor/tests/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ describe("Anchor: Transfer SOL", () => {
5858
payer: payerAccount.publicKey,
5959
recipient: recipientAccount.publicKey,
6060
})
61+
.signers([payerAccount])
6162
.rpc();
6263

6364
const recipientBalance = await provider.connection.getBalance(

0 commit comments

Comments
 (0)