Skip to main content

sealevel_tools/cpi/system_program/
transfer.rs

1use crate::{
2    account::system::ID,
3    cpi::{CpiAuthority, CpiInstruction},
4    entrypoint::NoStdAccountInfo,
5};
6
7/// Arguments for the transfer instruction on the System program, which moves lamports between two
8/// accounts.
9#[derive(Clone, PartialEq, Eq)]
10pub struct Transfer<'a, 'b: 'a> {
11    pub from: CpiAuthority<'a, 'b>,
12    pub to: &'b NoStdAccountInfo,
13    pub lamports: u64,
14}
15
16impl<'a, 'b: 'a> Transfer<'a, 'b> {
17    /// Consume arguments to perform CPI call.
18    #[inline(always)]
19    pub fn into_invoke(self) {
20        let Self { from, to, lamports } = self;
21
22        _invoke_transfer(
23            &from,
24            &CpiAuthority {
25                account: to,
26                signer_seeds: None,
27            },
28            lamports,
29        );
30    }
31}
32
33#[inline(always)]
34pub(super) fn _invoke_transfer(from: &CpiAuthority, to: &CpiAuthority, lamports: u64) {
35    let instruction_data = _serialize_instruction_data(lamports);
36
37    let from_account = from.account;
38    let to_account = to.account;
39
40    CpiInstruction {
41        program_id: &ID,
42        accounts: &[from_account.to_meta_c(), to_account.to_meta_c_signer()],
43        data: &instruction_data,
44    }
45    .invoke_possibly_signed(
46        &[from_account.to_info_c(), to_account.to_info_c()],
47        &[from.signer_seeds, to.signer_seeds],
48    );
49}
50
51const IX_DATA_LEN: usize = {
52    4 // selector
53    + core::mem::size_of::<u64>() // lamports
54};
55
56#[inline(always)]
57fn _serialize_instruction_data(lamports: u64) -> [u8; IX_DATA_LEN] {
58    let mut instruction_data = [0; IX_DATA_LEN];
59
60    // Transfer selector == 2.
61    instruction_data[0] = 2;
62    instruction_data[4..12].copy_from_slice(&lamports.to_le_bytes());
63
64    instruction_data
65}
66
67#[cfg(test)]
68mod test {
69    use solana_sdk::system_instruction::SystemInstruction;
70
71    use super::*;
72
73    #[test]
74    fn test_serialize_instruction_data() {
75        let lamports = 69;
76
77        let instruction_data = _serialize_instruction_data(lamports);
78
79        assert_eq!(
80            bincode::deserialize::<SystemInstruction>(&instruction_data).unwrap(),
81            SystemInstruction::Transfer { lamports }
82        );
83    }
84}