Skip to content

Commit 1818715

Browse files
author
jpcaulfi
committed
transfer
1 parent e76ac6c commit 1818715

File tree

8 files changed

+320
-101
lines changed

8 files changed

+320
-101
lines changed
Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
use anchor_lang::prelude::*;
22
use anchor_lang::system_program;
33

4+
45
declare_id!("4fQVnLWKKKYxtxgGn7Haw8v2g2Hzbu8K61JvWKvqAi7W");
56

7+
68
#[program]
79
pub mod transfer_sol {
810
use super::*;
911

10-
pub fn transfer_sol(ctx: Context<TransferSol>, amount: u64) -> Result<()> {
11-
12-
msg!("Received request to transfer {:?} lamports from {:?} to {:?}.",
13-
amount, &ctx.accounts.payer.key(), &ctx.accounts.recipient.key());
14-
msg!(" Processing transfer...");
12+
pub fn transfer_sol_with_cpi(
13+
ctx: Context<TransferSolWithCpi>,
14+
amount: u64
15+
) -> Result<()> {
1516

1617
system_program::transfer(
1718
CpiContext::new(
@@ -23,18 +24,42 @@ pub mod transfer_sol {
2324
),
2425
amount,
2526
)?;
27+
28+
Ok(())
29+
}
30+
31+
pub fn transfer_sol_with_program(
32+
ctx: Context<TransferSolWithProgram>,
33+
amount: u64
34+
) -> Result<()> {
35+
36+
**ctx.accounts.payer
37+
.to_account_info()
38+
.try_borrow_mut_lamports()? -= amount;
39+
**ctx.accounts.recipient
40+
.to_account_info()
41+
.try_borrow_mut_lamports()? += amount;
2642

27-
msg!("Transfer completed successfully.");
2843
Ok(())
2944
}
3045
}
3146

3247
#[derive(Accounts)]
33-
pub struct TransferSol<'info> {
34-
/// CHECK: We're initializing this account via the transfer
48+
pub struct TransferSolWithCpi<'info> {
3549
#[account(mut)]
36-
recipient: AccountInfo<'info>,
50+
recipient: SystemAccount<'info>,
3751
#[account(mut)]
3852
payer: Signer<'info>,
3953
system_program: Program<'info, System>,
40-
}
54+
}
55+
56+
#[derive(Accounts)]
57+
pub struct TransferSolWithProgram<'info> {
58+
/// CHECK: This is just an example, not checking data
59+
#[account(mut)]
60+
recipient: UncheckedAccount<'info>,
61+
/// CHECK: This is just an example, not checking data
62+
#[account(mut)]
63+
payer: UncheckedAccount<'info>,
64+
system_program: Program<'info, System>,
65+
}

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

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,79 @@ describe("transfer-sol", () => {
55

66
const provider = anchor.AnchorProvider.env();
77
anchor.setProvider(provider);
8-
const wallet = provider.wallet as anchor.Wallet;
8+
const payer = provider.wallet as anchor.Wallet;
99
const program = anchor.workspace.TransferSol as anchor.Program<TransferSol>;
1010

11-
it("Transfer some SOL", async () => {
11+
const transferAmount = 1 * anchor.web3.LAMPORTS_PER_SOL;
12+
const test1Recipient = anchor.web3.Keypair.generate();
13+
const test2Recipient1 = anchor.web3.Keypair.generate();
14+
const test2Recipient2 = anchor.web3.Keypair.generate();
1215

13-
async function getBalances(payerPubkey: anchor.web3.PublicKey, recipientPubkey: anchor.web3.PublicKey, timeframe: string) {
14-
let payerBalance = await provider.connection.getBalance(payerPubkey);
15-
let recipientBalance = await provider.connection.getBalance(recipientPubkey);
16-
console.log(`${timeframe} balances:`);
17-
console.log(` Payer: ${payerBalance}`);
18-
console.log(` Recipient: ${recipientBalance}`);
16+
it("Transfer between accounts using the system program", async () => {
17+
18+
await getBalances(payer.publicKey, test1Recipient.publicKey, "Beginning");
19+
20+
await program.methods.transferSolWithCpi(new anchor.BN(transferAmount))
21+
.accounts({
22+
payer: payer.publicKey,
23+
recipient: test1Recipient.publicKey,
24+
systemProgram: anchor.web3.SystemProgram.programId,
25+
})
26+
.signers([payer.payer])
27+
.rpc();
28+
29+
await getBalances(payer.publicKey, test1Recipient.publicKey, "Resulting");
30+
31+
});
32+
33+
it("Create two accounts for the following test", async () => {
34+
35+
const ix = (pubkey: anchor.web3.PublicKey) => {
36+
return anchor.web3.SystemProgram.createAccount({
37+
fromPubkey: payer.publicKey,
38+
newAccountPubkey: pubkey,
39+
space: 0,
40+
lamports: 2 * anchor.web3.LAMPORTS_PER_SOL,
41+
programId: program.programId,
42+
})
1943
};
2044

21-
const recipientKeypair = anchor.web3.Keypair.generate();
22-
const transferAmount = 1 * anchor.web3.LAMPORTS_PER_SOL;
45+
await anchor.web3.sendAndConfirmTransaction(
46+
provider.connection,
47+
new anchor.web3.Transaction()
48+
.add(ix(test2Recipient1.publicKey))
49+
.add(ix(test2Recipient2.publicKey))
50+
,
51+
[payer.payer, test2Recipient1, test2Recipient2]
52+
);
53+
});
54+
55+
it("Transfer between accounts using our program", async () => {
2356

24-
await getBalances(wallet.publicKey, recipientKeypair.publicKey, "Beginning");
57+
await getBalances(test2Recipient1.publicKey, test2Recipient2.publicKey, "Beginning");
2558

26-
await program.methods.transferSol(new anchor.BN(transferAmount))
27-
.accounts({
28-
payer: wallet.publicKey,
29-
recipient: recipientKeypair.publicKey,
30-
systemProgram: anchor.web3.SystemProgram.programId,
31-
})
32-
.signers([wallet.payer])
33-
.rpc();
59+
await program.methods.transferSolWithProgram(new anchor.BN(transferAmount))
60+
.accounts({
61+
payer: test2Recipient1.publicKey,
62+
recipient: test2Recipient2.publicKey,
63+
systemProgram: anchor.web3.SystemProgram.programId,
64+
})
65+
.rpc();
3466

35-
await getBalances(wallet.publicKey, recipientKeypair.publicKey, "Resulting");
67+
await getBalances(test2Recipient1.publicKey, test2Recipient2.publicKey, "Resulting");
3668

3769
});
70+
71+
async function getBalances(
72+
payerPubkey: anchor.web3.PublicKey,
73+
recipientPubkey: anchor.web3.PublicKey,
74+
timeframe: string
75+
) {
76+
77+
let payerBalance = await provider.connection.getBalance(payerPubkey);
78+
let recipientBalance = await provider.connection.getBalance(recipientPubkey);
79+
console.log(`${timeframe} balances:`);
80+
console.log(` Payer: ${payerBalance}`);
81+
console.log(` Recipient: ${recipientBalance}`);
82+
};
3883
});

basics/transfer-sol/native/program/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7+
borsh = "0.9.3"
8+
borsh-derive = "0.9.1"
79
solana-program = "1.10.12"
810

911
[lib]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use solana_program::{
2+
account_info::{ AccountInfo, next_account_info },
3+
entrypoint::ProgramResult,
4+
program::invoke,
5+
pubkey::Pubkey,
6+
system_instruction,
7+
};
8+
9+
10+
pub fn transfer_sol_with_cpi(
11+
accounts: &[AccountInfo],
12+
amount: u64,
13+
) -> ProgramResult {
14+
15+
let accounts_iter = &mut accounts.iter();
16+
let payer = next_account_info(accounts_iter)?;
17+
let recipient = next_account_info(accounts_iter)?;
18+
let system_program = next_account_info(accounts_iter)?;
19+
20+
invoke(
21+
&system_instruction::transfer(payer.key, recipient.key, amount),
22+
&[payer.clone(), recipient.clone(), system_program.clone()],
23+
)?;
24+
25+
Ok(())
26+
}
27+
28+
pub fn transfer_sol_with_program(
29+
program_id: &Pubkey,
30+
accounts: &[AccountInfo],
31+
amount: u64,
32+
) -> ProgramResult {
33+
34+
let accounts_iter = &mut accounts.iter();
35+
let payer = next_account_info(accounts_iter)?;
36+
let recipient = next_account_info(accounts_iter)?;
37+
38+
**payer.try_borrow_mut_lamports()? -= amount;
39+
**recipient.try_borrow_mut_lamports()? += amount;
40+
41+
Ok(())
42+
}
Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,10 @@
1-
use {
2-
std::convert::TryInto,
3-
solana_program::{
4-
account_info::{
5-
next_account_info, AccountInfo
6-
},
7-
entrypoint,
8-
entrypoint::ProgramResult,
9-
msg,
10-
program::invoke,
11-
program_error::ProgramError,
12-
pubkey::Pubkey,
13-
system_instruction,
14-
},
15-
};
16-
17-
18-
entrypoint!(process_instruction);
19-
201

21-
fn process_instruction(
22-
_program_id: &Pubkey,
23-
accounts: &[AccountInfo],
24-
instruction_data: &[u8],
25-
) -> ProgramResult {
2+
pub mod instruction;
3+
pub mod processor;
264

27-
let accounts_iter = &mut accounts.iter();
28-
let payer = next_account_info(accounts_iter)?;
29-
let recipient = next_account_info(accounts_iter)?;
30-
31-
let amount = instruction_data
32-
.get(..8)
33-
.and_then(|slice| slice.try_into().ok())
34-
.map(u64::from_le_bytes)
35-
.ok_or(ProgramError::InvalidInstructionData)?;
36-
37-
msg!("Received request to transfer {:?} lamports from {:?} to {:?}.",
38-
amount, payer.key, recipient.key);
39-
msg!(" Processing transfer...");
5+
use {
6+
solana_program::entrypoint,
7+
crate::processor::process_instruction,
8+
};
409

41-
invoke(
42-
&system_instruction::transfer(payer.key, recipient.key, amount),
43-
&[payer.clone(), recipient.clone()],
44-
)?;
45-
46-
msg!("Transfer completed successfully.");
47-
Ok(())
48-
}
10+
entrypoint!(process_instruction);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use borsh::{ BorshDeserialize, BorshSerialize };
2+
use solana_program::{
3+
account_info::AccountInfo,
4+
entrypoint::ProgramResult,
5+
pubkey::Pubkey,
6+
};
7+
8+
use crate::instruction::transfer_sol_with_cpi;
9+
use crate::instruction::transfer_sol_with_program;
10+
11+
12+
#[derive(BorshSerialize, BorshDeserialize, Debug)]
13+
pub enum TransferInstruction {
14+
CpiTransfer(u64),
15+
ProgramTransfer(u64),
16+
}
17+
18+
19+
pub fn process_instruction(
20+
program_id: &Pubkey,
21+
accounts: &[AccountInfo],
22+
input: &[u8],
23+
) -> ProgramResult {
24+
25+
let instruction = TransferInstruction::try_from_slice(&input)?;
26+
match instruction {
27+
TransferInstruction::CpiTransfer(
28+
args) => transfer_sol_with_cpi(accounts, args),
29+
TransferInstruction::ProgramTransfer(
30+
args) => transfer_sol_with_program(program_id, accounts, args),
31+
}
32+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import * as borsh from "borsh";
2+
import { Buffer } from "buffer";
3+
import {
4+
PublicKey,
5+
SystemProgram,
6+
TransactionInstruction
7+
} from '@solana/web3.js';
8+
9+
10+
export enum InstructionType {
11+
CpiTransfer,
12+
ProgramTransfer,
13+
}
14+
15+
16+
export class TransferInstruction {
17+
18+
instruction: InstructionType;
19+
amount: number;
20+
21+
constructor(props: {
22+
instruction: InstructionType,
23+
amount: number,
24+
}) {
25+
this.instruction = props.instruction;
26+
this.amount = props.amount;
27+
}
28+
29+
toBuffer() {
30+
return Buffer.from(borsh.serialize(TransferInstructionSchema, this))
31+
};
32+
33+
static fromBuffer(buffer: Buffer) {
34+
return borsh.deserialize(TransferInstructionSchema, TransferInstruction, buffer);
35+
};
36+
};
37+
38+
export const TransferInstructionSchema = new Map([
39+
[ TransferInstruction, {
40+
kind: 'struct',
41+
fields: [
42+
['instruction', 'u8'],
43+
['amount', 'u64'],
44+
],
45+
}]
46+
]);
47+
48+
export function createTransferInstruction(
49+
payerPubkey: PublicKey,
50+
recipientPubkey: PublicKey,
51+
programId: PublicKey,
52+
instruction: InstructionType,
53+
amount: number,
54+
): TransactionInstruction {
55+
56+
const instructionObject = new TransferInstruction({
57+
instruction,
58+
amount,
59+
});
60+
61+
const ix = new TransactionInstruction({
62+
keys: [
63+
{pubkey: payerPubkey, isSigner: true, isWritable: true},
64+
{pubkey: recipientPubkey, isSigner: false, isWritable: true},
65+
{pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
66+
],
67+
programId,
68+
data: instructionObject.toBuffer(),
69+
});
70+
71+
return ix;
72+
}

0 commit comments

Comments
 (0)