Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 6d71e89

Browse files
committed
Few more instructions
1 parent 81fc224 commit 6d71e89

21 files changed

+1080
-24
lines changed

README.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
<a href="https://github.com/febo/p-token/actions/workflows/main.yml"><img src="https://img.shields.io/github/actions/workflow/status/febo/p-token/main.yml?logo=GitHub" /></a>
1313
</p>
1414

15+
> [!WARNING]
16+
> The program is not yet fully-optimized. There are still opportunities to improve the compute units consumption.
17+
1518
## Overview
1619

1720
This repository contains a **proof-of-concept** of a reimplementation of the SPL Token program, one of the most used programs on Solana, using [`pinocchio`](https://github.com/febo/pinocchio). The purpose is to have an implementation that optimizes the compute units, while being fully compatible with the original implementation &mdash; i.e., support the exact same instruction and account layouts as SPL Token, byte for byte.
@@ -22,7 +25,7 @@ This repository contains a **proof-of-concept** of a reimplementation of the SPL
2225
|----------------------------|-----------|----------------|------------------|
2326
| `InitializeMint` || 370 | 2967 |
2427
| `InitializeAccount` || 406 | 4527 |
25-
| `InitializeMultisig` || 431 | 2973 |
28+
| `InitializeMultisig` || 431 | 2973 |
2629
| `Transfer` || 161 | 4645 |
2730
| `Approve` || 123 | 2904 |
2831
| `Revoke` || 88 | 2677 |
@@ -32,15 +35,15 @@ This repository contains a **proof-of-concept** of a reimplementation of the SPL
3235
| `CloseAccount` || 162 | 2916 |
3336
| `FreezeAccount` || 128 | 4265 |
3437
| `ThawAccount` || 129 | 4267 |
35-
| `TransferChecked` | | | |
36-
| `ApproveChecked` | | | |
37-
| `MintToChecked` | | | |
38-
| `BurnChecked` | | | |
39-
| `InitializeAccount2` | | | |
38+
| `TransferChecked` | | 275 | 6201 |
39+
| `ApproveChecked` | | 222 | 4459 |
40+
| `MintToChecked` | | 218 | 4546 |
41+
| `BurnChecked` | | 201 | 4755 |
42+
| `InitializeAccount2` | | 431 | 4388 |
4043
| `SyncNative` | | | |
41-
| `InitializeAccount3` | | | |
42-
| `InitializeMultisig2` | | | |
43-
| `InitializeMint2` | | | |
44+
| `InitializeAccount3` | | 566 | 4240 |
45+
| `InitializeMultisig2` | | 601 | 2826 |
46+
| `InitializeMint2` | | 529 | 2827 |
4447
| `GetAccountDataSize` | | | |
4548
| `InitializeImmutableOwner` | | | |
4649
| `AmountToUiAmount` | | | |

program/src/entrypoint.rs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,25 @@ use pinocchio::{
55

66
use crate::processor::{
77
approve::process_approve,
8+
approve_checked::{process_approve_checked, ApproveChecked},
89
burn::process_burn,
10+
burn_checked::{process_burn_checked, BurnChecked},
911
close_account::process_close_account,
1012
freeze_account::process_freeze_account,
1113
initialize_account::process_initialize_account,
14+
initialize_account2::process_initialize_account2,
15+
initialize_account3::process_initialize_account3,
1216
initialize_mint::{process_initialize_mint, InitializeMint},
17+
initialize_mint2::process_initialize_mint2,
1318
initialize_multisig::process_initialize_multisig,
19+
initialize_multisig2::process_initialize_multisig2,
1420
mint_to::process_mint_to,
21+
mint_to_checked::{process_mint_to_checked, MintToChecked},
1522
revoke::process_revoke,
1623
set_authority::{process_set_authority, SetAuthority},
1724
thaw_account::process_thaw_account,
1825
transfer::process_transfer,
26+
transfer_checked::{process_transfer_checked, TransferChecked},
1927
};
2028

2129
entrypoint!(process_instruction);
@@ -33,6 +41,7 @@ pub fn process_instruction(
3341
pinocchio::msg!("Instruction: InitializeMint");
3442

3543
let instruction = InitializeMint::try_from_bytes(data)?;
44+
3645
process_initialize_mint(accounts, &instruction, true)
3746
}
3847
// 1 - InitializeAccount
@@ -133,13 +142,85 @@ pub fn process_instruction(
133142

134143
process_freeze_account(program_id, accounts)
135144
}
136-
// 10 - ThawAccount
145+
// 11 - ThawAccount
137146
Some((&11, _)) => {
138147
#[cfg(feature = "logging")]
139148
pinocchio::msg!("Instruction: ThawAccount");
140149

141150
process_thaw_account(program_id, accounts)
142151
}
152+
// 12 - TransferChecked
153+
Some((&12, data)) => {
154+
#[cfg(feature = "logging")]
155+
pinocchio::msg!("Instruction: TransferChecked");
156+
157+
let args = TransferChecked::try_from_bytes(data)?;
158+
159+
process_transfer_checked(program_id, accounts, args.amount(), args.decimals())
160+
}
161+
// 13 - ApproveChecked
162+
Some((&13, data)) => {
163+
#[cfg(feature = "logging")]
164+
pinocchio::msg!("Instruction: ApproveChecked");
165+
166+
let args = ApproveChecked::try_from_bytes(data)?;
167+
168+
process_approve_checked(program_id, accounts, args.amount(), args.decimals())
169+
}
170+
// 14 - MintToChecked
171+
Some((&14, data)) => {
172+
#[cfg(feature = "logging")]
173+
pinocchio::msg!("Instruction: MintToChecked");
174+
175+
let args = MintToChecked::try_from_bytes(data)?;
176+
177+
process_mint_to_checked(program_id, accounts, args.amount(), args.decimals())
178+
}
179+
// 15 - BurnChecked
180+
Some((&15, data)) => {
181+
#[cfg(feature = "logging")]
182+
pinocchio::msg!("Instruction: BurnChecked");
183+
184+
let args = BurnChecked::try_from_bytes(data)?;
185+
186+
process_burn_checked(program_id, accounts, args.amount(), args.decimals())
187+
}
188+
// 16 - InitializeAccount2
189+
Some((&16, data)) => {
190+
#[cfg(feature = "logging")]
191+
pinocchio::msg!("Instruction: InitializeAccount2");
192+
193+
let owner = unsafe { &*(data.as_ptr() as *const Pubkey) };
194+
195+
process_initialize_account2(program_id, accounts, owner)
196+
}
197+
// 18 - InitializeAccount3
198+
Some((&18, data)) => {
199+
#[cfg(feature = "logging")]
200+
pinocchio::msg!("Instruction: InitializeAccount3");
201+
202+
let owner = unsafe { &*(data.as_ptr() as *const Pubkey) };
203+
204+
process_initialize_account3(program_id, accounts, owner)
205+
}
206+
// 19 - InitializeMultisig2
207+
Some((&19, data)) => {
208+
#[cfg(feature = "logging")]
209+
pinocchio::msg!("Instruction: InitializeMultisig2");
210+
211+
let m = data.first().ok_or(ProgramError::InvalidInstructionData)?;
212+
213+
process_initialize_multisig2(accounts, *m)
214+
}
215+
// 20 - InitializeMint2
216+
Some((&20, data)) => {
217+
#[cfg(feature = "logging")]
218+
pinocchio::msg!("Instruction: InitializeMint2");
219+
220+
let instruction = InitializeMint::try_from_bytes(data)?;
221+
222+
process_initialize_mint2(accounts, &instruction)
223+
}
143224
_ => Err(ProgramError::InvalidInstructionData),
144225
}
145226
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::marker::PhantomData;
2+
3+
use pinocchio::{
4+
account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
5+
};
6+
7+
use super::approve::process_approve;
8+
9+
#[inline(always)]
10+
pub fn process_approve_checked(
11+
program_id: &Pubkey,
12+
accounts: &[AccountInfo],
13+
amount: u64,
14+
decimals: u8,
15+
) -> ProgramResult {
16+
process_approve(program_id, accounts, amount, Some(decimals))
17+
}
18+
19+
pub struct ApproveChecked<'a> {
20+
raw: *const u8,
21+
22+
_data: PhantomData<&'a [u8]>,
23+
}
24+
25+
impl ApproveChecked<'_> {
26+
pub fn try_from_bytes(bytes: &[u8]) -> Result<ApproveChecked, ProgramError> {
27+
if bytes.len() != 9 {
28+
return Err(ProgramError::InvalidInstructionData);
29+
}
30+
31+
Ok(ApproveChecked {
32+
raw: bytes.as_ptr(),
33+
_data: PhantomData,
34+
})
35+
}
36+
37+
pub fn amount(&self) -> u64 {
38+
unsafe {
39+
let amount = self.raw as *const u64;
40+
amount.read_unaligned()
41+
}
42+
}
43+
44+
pub fn decimals(&self) -> u8 {
45+
unsafe { *self.raw.add(8) }
46+
}
47+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::marker::PhantomData;
2+
3+
use pinocchio::{
4+
account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
5+
};
6+
7+
use super::burn::process_burn;
8+
9+
#[inline(always)]
10+
pub fn process_burn_checked(
11+
program_id: &Pubkey,
12+
accounts: &[AccountInfo],
13+
amount: u64,
14+
decimals: u8,
15+
) -> ProgramResult {
16+
process_burn(program_id, accounts, amount, Some(decimals))
17+
}
18+
19+
pub struct BurnChecked<'a> {
20+
raw: *const u8,
21+
22+
_data: PhantomData<&'a [u8]>,
23+
}
24+
25+
impl BurnChecked<'_> {
26+
pub fn try_from_bytes(bytes: &[u8]) -> Result<BurnChecked, ProgramError> {
27+
if bytes.len() != 9 {
28+
return Err(ProgramError::InvalidInstructionData);
29+
}
30+
31+
Ok(BurnChecked {
32+
raw: bytes.as_ptr(),
33+
_data: PhantomData,
34+
})
35+
}
36+
37+
pub fn amount(&self) -> u64 {
38+
unsafe {
39+
let amount = self.raw as *const u64;
40+
amount.read_unaligned()
41+
}
42+
}
43+
44+
pub fn decimals(&self) -> u8 {
45+
unsafe { *self.raw.add(8) }
46+
}
47+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
2+
3+
use super::initialize_account::process_initialize_account;
4+
5+
#[inline(always)]
6+
pub fn process_initialize_account2(
7+
program_id: &Pubkey,
8+
accounts: &[AccountInfo],
9+
owner: &Pubkey,
10+
) -> ProgramResult {
11+
process_initialize_account(program_id, accounts, Some(owner), true)
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
2+
3+
use super::initialize_account::process_initialize_account;
4+
5+
#[inline(always)]
6+
pub fn process_initialize_account3(
7+
program_id: &Pubkey,
8+
accounts: &[AccountInfo],
9+
owner: &Pubkey,
10+
) -> ProgramResult {
11+
process_initialize_account(program_id, accounts, Some(owner), false)
12+
}

program/src/processor/initialize_mint.rs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
use bytemuck::{Pod, Zeroable};
21
use pinocchio::{
32
account_info::AccountInfo,
43
program_error::ProgramError,
5-
pubkey::{Pubkey, PUBKEY_BYTES},
4+
pubkey::Pubkey,
65
sysvars::{rent::Rent, Sysvar},
76
ProgramResult,
87
};
9-
use std::mem::size_of;
8+
use std::{marker::PhantomData, mem::size_of};
109
use token_interface::{
1110
error::TokenError,
1211
state::{mint::Mint, PodCOption},
@@ -52,17 +51,18 @@ pub fn process_initialize_mint(
5251

5352
// Initialize the mint.
5453

55-
mint.mint_authority = PodCOption::from(Some(args.data.mint_authority));
56-
mint.decimals = args.data.decimals;
54+
mint.mint_authority = PodCOption::from(Some(*args.mint_authority()));
55+
mint.decimals = args.decimals();
5756
mint.is_initialized = true.into();
5857

59-
if let Some(freeze_authority) = args.freeze_authority {
58+
if let Some(freeze_authority) = args.freeze_authority() {
6059
mint.freeze_authority = PodCOption::from(Some(*freeze_authority));
6160
}
6261

6362
Ok(())
6463
}
6564

65+
/*
6666
/// Instruction data for the `InitializeMint` instruction.
6767
pub struct InitializeMint<'a> {
6868
pub data: &'a MintData,
@@ -107,3 +107,45 @@ pub struct MintData {
107107
/// The authority/multisignature to mint tokens.
108108
pub mint_authority: Pubkey,
109109
}
110+
*/
111+
/// Instruction data for the `InitializeMint2` instruction.
112+
pub struct InitializeMint<'a> {
113+
raw: *const u8,
114+
115+
_data: PhantomData<&'a [u8]>,
116+
}
117+
118+
impl InitializeMint<'_> {
119+
pub fn try_from_bytes(bytes: &[u8]) -> Result<InitializeMint, ProgramError> {
120+
// The minimum expected size of the instruction data.
121+
// - decimals (1 byte)
122+
// - mint_authority (32 bytes)
123+
// - option + freeze_authority (1 byte + 32 bytes)
124+
if bytes.len() < 34 {
125+
return Err(ProgramError::InvalidInstructionData);
126+
}
127+
128+
Ok(InitializeMint {
129+
raw: bytes.as_ptr(),
130+
_data: PhantomData,
131+
})
132+
}
133+
134+
pub fn decimals(&self) -> u8 {
135+
unsafe { *self.raw }
136+
}
137+
138+
pub fn mint_authority(&self) -> &Pubkey {
139+
unsafe { &*(self.raw.add(1) as *const Pubkey) }
140+
}
141+
142+
pub fn freeze_authority(&self) -> Option<&Pubkey> {
143+
unsafe {
144+
if *self.raw.add(33) == 0 {
145+
Option::None
146+
} else {
147+
Option::Some(&*(self.raw.add(34) as *const Pubkey))
148+
}
149+
}
150+
}
151+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use pinocchio::{account_info::AccountInfo, ProgramResult};
2+
3+
use super::initialize_mint::{process_initialize_mint, InitializeMint};
4+
5+
pub fn process_initialize_mint2(accounts: &[AccountInfo], args: &InitializeMint) -> ProgramResult {
6+
process_initialize_mint(accounts, args, false)
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use pinocchio::{account_info::AccountInfo, ProgramResult};
2+
3+
use super::initialize_multisig::process_initialize_multisig;
4+
5+
pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult {
6+
process_initialize_multisig(accounts, m, false)
7+
}

0 commit comments

Comments
 (0)