Skip to content

Commit 2e54ff1

Browse files
committed
feat: Added pinocchio version of transfer-sol program example
1 parent c69a7bb commit 2e54ff1

File tree

9 files changed

+244
-0
lines changed

9 files changed

+244
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ members = [
3232
"basics/repository-layout/anchor/programs/*",
3333
"basics/transfer-sol/native/program",
3434
"basics/transfer-sol/anchor/programs/*",
35+
"basics/transfer-sol/pinocchio/program",
3536
"tokens/token-2022/mint-close-authority/native/program",
3637
"tokens/token-2022/non-transferable/native/program",
3738
"tokens/token-2022/default-account-state/native/program",
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"scripts": {
3+
"test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./test/transferSol.test.ts",
4+
"build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test",
5+
"build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so",
6+
"deploy": "solana program deploy ./program/target/so/hello_solana_program_pinocchio.so"
7+
},
8+
"dependencies": {
9+
"@solana/web3.js": "^1.47.3"
10+
},
11+
"devDependencies": {
12+
"@types/bn.js": "^5.1.0",
13+
"@types/chai": "^4.3.1",
14+
"@types/mocha": "^9.1.1",
15+
"@types/node": "^22.15.2",
16+
"chai": "^4.3.4",
17+
"mocha": "^9.0.3",
18+
"solana-bankrun": "^0.3.0",
19+
"ts-mocha": "^10.0.0",
20+
"typescript": "^4.3.5"
21+
}
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "pinocchio-transfer-sol"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
crate-type = ["cdylib", "lib"]
8+
9+
[dependencies]
10+
bytemuck = {version = "1.24.0", features = ["derive"]}
11+
pinocchio = "0.9.2"
12+
pinocchio-pubkey = "0.3.0"
13+
pinocchio-system = "0.3.0"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use bytemuck::{Pod, Zeroable};
2+
use {
3+
pinocchio::{
4+
account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
5+
},
6+
pinocchio_system::instructions::Transfer,
7+
};
8+
#[repr(C)]
9+
#[derive(Pod, Zeroable, Clone, Copy)]
10+
pub struct CpiTransferArgs {
11+
amount: u64,
12+
}
13+
14+
pub fn process_cpi_transfer(
15+
_program_id: &Pubkey,
16+
accounts: &[AccountInfo],
17+
ix_data: &[u8],
18+
) -> ProgramResult {
19+
msg!("TransferSol Instruction: CpiTransfer");
20+
if let [sender, receiver, _system_program] = accounts {
21+
let mut aligned_ix_buf = [0u8; core::mem::size_of::<CpiTransferArgs>()]; // putting raw ix_data will fail since it started at index 1 of the original instruction_data, so this new allocation is required
22+
23+
aligned_ix_buf.copy_from_slice(ix_data);
24+
25+
let params = bytemuck::try_from_bytes::<CpiTransferArgs>(&aligned_ix_buf)
26+
.map_err(|_| ProgramError::InvalidInstructionData)?;
27+
28+
// sol_log_64(params.amount, 0, 0, 0, 0);
29+
30+
Transfer {
31+
from: sender,
32+
lamports: params.amount,
33+
to: receiver,
34+
}
35+
.invoke()?;
36+
}
37+
38+
Ok(())
39+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use pinocchio::program_error::ProgramError;
2+
3+
pub mod cpi_transfer;
4+
5+
#[repr(u8)]
6+
pub enum TransferSolInstructions {
7+
CpiTransfer,
8+
}
9+
10+
impl TryFrom<&u8> for TransferSolInstructions {
11+
type Error = ProgramError;
12+
13+
fn try_from(value: &u8) -> Result<Self, Self::Error> {
14+
match *value {
15+
0 => Ok(TransferSolInstructions::CpiTransfer),
16+
_ => Err(ProgramError::InvalidInstructionData),
17+
}
18+
}
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![no_std]
2+
use pinocchio::{no_allocator, nostd_panic_handler, program_entrypoint};
3+
use pinocchio_pubkey::declare_id;
4+
mod processor;
5+
use processor::process_instruction;
6+
mod instructions;
7+
8+
declare_id!("8TpdLD58VBWsdzxRi2yRcmKJD9UcE2GuUrBwsyCwpbUN");
9+
10+
program_entrypoint!(process_instruction);
11+
no_allocator!();
12+
nostd_panic_handler!();
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use crate::instructions::{cpi_transfer::*, TransferSolInstructions};
2+
use pinocchio::{
3+
account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
4+
};
5+
6+
pub fn process_instruction(
7+
program_id: &Pubkey,
8+
accounts: &[AccountInfo],
9+
ix_data: &[u8],
10+
) -> ProgramResult {
11+
let (disc, ix_args) = ix_data
12+
.split_first()
13+
.ok_or(ProgramError::InvalidInstructionData)?;
14+
15+
match TransferSolInstructions::try_from(disc)? {
16+
TransferSolInstructions::CpiTransfer => {
17+
process_cpi_transfer(program_id, accounts, ix_args)?
18+
}
19+
}
20+
21+
Ok(())
22+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import {
2+
PublicKey,
3+
Transaction,
4+
TransactionInstruction,
5+
Keypair,
6+
SystemProgram,
7+
LAMPORTS_PER_SOL,
8+
} from "@solana/web3.js";
9+
import pkg from "@solana/web3.js";
10+
import { assert } from "chai";
11+
import { test } from "mocha";
12+
import { ProgramTestContext, start } from "solana-bankrun";
13+
import BN from "bn.js";
14+
describe("Transfer Sol Tests", () => {
15+
const programId = new PublicKey(
16+
"8TpdLD58VBWsdzxRi2yRcmKJD9UcE2GuUrBwsyCwpbUN",
17+
);
18+
19+
test("Test Cpi Transfer Works", async () => {
20+
const context = await start(
21+
[{ programId, name: "./program/target/deploy/pinocchio_transfer_sol" }],
22+
[],
23+
);
24+
25+
const client = context.banksClient;
26+
const payer = context.payer;
27+
const blockhash = context.lastBlockhash;
28+
29+
const param = { amount: LAMPORTS_PER_SOL };
30+
31+
const ix_data_buffer = Buffer.concat([
32+
Uint8Array.from([0]),
33+
Uint8Array.from(new BN(param.amount).toArray("le", 8)),
34+
]);
35+
const sender = Keypair.generate();
36+
const receiver = PublicKey.unique();
37+
38+
const initialCredit: any = {
39+
fromPubkey: payer.publicKey,
40+
lamports: 1.5 * LAMPORTS_PER_SOL,
41+
toPubkey: sender.publicKey,
42+
};
43+
44+
const ix1 = [SystemProgram.transfer(initialCredit)];
45+
46+
const tx1 = new Transaction();
47+
tx1.recentBlockhash = blockhash;
48+
tx1.add(...ix1);
49+
50+
tx1.sign(payer);
51+
52+
await client.simulateTransaction(tx1);
53+
await client.processTransaction(tx1);
54+
const ix2 = [
55+
new TransactionInstruction({
56+
programId,
57+
keys: [
58+
{ pubkey: sender.publicKey, isSigner: true, isWritable: true },
59+
{ pubkey: receiver, isSigner: false, isWritable: true },
60+
{
61+
pubkey: SystemProgram.programId,
62+
isSigner: false,
63+
isWritable: false,
64+
},
65+
],
66+
data: ix_data_buffer,
67+
}),
68+
];
69+
70+
const tx2 = new Transaction();
71+
tx2.recentBlockhash = blockhash;
72+
tx2.add(...ix2);
73+
tx2.feePayer = payer.publicKey;
74+
tx2.sign(sender, payer);
75+
76+
const senderBalanceBefore = await client.getBalance(
77+
sender.publicKey,
78+
"finalized",
79+
);
80+
await client.simulateTransaction(tx2, "finalized");
81+
const processedTxn = await client.processTransaction(tx2);
82+
83+
assert(
84+
processedTxn.logMessages[0].startsWith(`Program ${programId.toString()}`),
85+
);
86+
87+
assert(
88+
processedTxn.logMessages[processedTxn.logMessages.length - 1] ==
89+
`Program ${programId.toString()} success`,
90+
);
91+
92+
const senderBalanceAfter = await client.getBalance(
93+
sender.publicKey,
94+
"finalized",
95+
);
96+
const receiverBalanceAfter = await client.getBalance(receiver, "finalized");
97+
98+
assert(receiverBalanceAfter.toString() == param.amount.toString());
99+
100+
assert(
101+
new BN(senderBalanceBefore)
102+
.sub(new BN(senderBalanceAfter))
103+
.eq(new BN(param.amount)),
104+
);
105+
});
106+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"types": ["mocha", "chai", "node"],
4+
"typeRoots": ["./node_modules/@types"],
5+
"lib": ["es2015"],
6+
"module": "commonjs",
7+
"target": "es6",
8+
"esModuleInterop": true
9+
}
10+
}

0 commit comments

Comments
 (0)