Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Optimized Spl-token program #31

Merged
merged 22 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
resolver = "2"
members = [
"programs/system",
"programs/token",
"sdk/pinocchio",
"sdk/pubkey",
]
12 changes: 12 additions & 0 deletions programs/token/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "pinocchio-token"
description = "Pinocchio helpers to invoke Token program instructions"
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
readme = "./README.md"
repository = "https://github.com/febo/pinocchio"

[dependencies]
pinocchio = { version="0.6", path="../../sdk/pinocchio" }
pinocchio-pubkey = { version="0.2.1", path="../../sdk/pubkey" }
65 changes: 65 additions & 0 deletions programs/token/src/instructions/approve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use core::slice::from_raw_parts;

use pinocchio::{
account_info::AccountInfo,
instruction::{AccountMeta, Instruction, Signer},
program::invoke_signed,
ProgramResult,
};

use crate::{write_bytes, UNINIT_BYTE};

/// Approves a delegate.
///
/// ### Accounts:
/// 0. `[WRITE]` The token account.
/// 1. `[]` The delegate.
/// 2. `[SIGNER]` The source account owner.
pub struct Approve<'a> {
/// Source Account.
pub token: &'a AccountInfo,
/// Delegate Account
pub delegate: &'a AccountInfo,
/// Source Owner Account
pub authority: &'a AccountInfo,
/// Amount
pub amount: u64,
}

impl<'a> Approve<'a> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}

pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
// Account metadata
let account_metas: [AccountMeta; 3] = [
AccountMeta::writable(self.token.key()),
AccountMeta::readonly(self.delegate.key()),
AccountMeta::readonly_signer(self.authority.key()),
];

// Instruction data
// - [0]: instruction discriminator (1 byte, u8)
// - [1..9]: amount (8 bytes, u64)
let mut instruction_data = [UNINIT_BYTE; 9];

// Set discriminator as u8 at offset [0]
write_bytes(&mut instruction_data, &[4]);
// Set amount as u64 at offset [1..9]
write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes());

let instruction = Instruction {
program_id: &crate::ID,
accounts: &account_metas,
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just slice here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an array of [MaybeUninit<u8>] so we need to cast it to [u8].

};

invoke_signed(
&instruction,
&[self.token, self.delegate, self.authority],
signers,
)
}
}
74 changes: 74 additions & 0 deletions programs/token/src/instructions/approve_checked.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use core::slice::from_raw_parts;

use pinocchio::{
account_info::AccountInfo,
instruction::{AccountMeta, Instruction, Signer},
program::invoke_signed,
ProgramResult,
};

use crate::{write_bytes, UNINIT_BYTE};

/// Approves a delegate.
///
/// ### Accounts:
/// 0. `[WRITE]` The source account.
/// 1. `[]` The token mint.
/// 2. `[]` The delegate.
/// 3. `[SIGNER]` The source account owner.
pub struct ApproveChecked<'a> {
/// Source Account.
pub token: &'a AccountInfo,
/// Mint Account.
pub mint: &'a AccountInfo,
/// Delegate Account.
pub delegate: &'a AccountInfo,
/// Source Owner Account.
pub authority: &'a AccountInfo,
/// Amount.
pub amount: u64,
/// Decimals.
pub decimals: u8,
}

impl<'a> ApproveChecked<'a> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}

pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
// Account metadata
let account_metas: [AccountMeta; 4] = [
AccountMeta::writable(self.token.key()),
AccountMeta::readonly(self.mint.key()),
AccountMeta::readonly(self.delegate.key()),
AccountMeta::readonly_signer(self.authority.key()),
];

// Instruction data
// - [0] : instruction discriminator (1 byte, u8)
// - [1..9]: amount (8 bytes, u64)
// - [9] : decimals (1 byte, u8)
let mut instruction_data = [UNINIT_BYTE; 10];

// Set discriminator as u8 at offset [0]
write_bytes(&mut instruction_data, &[13]);
// Set amount as u64 at offset [1..9]
write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes());
// Set decimals as u8 at offset [9]
write_bytes(&mut instruction_data[9..], &[self.decimals]);

let instruction = Instruction {
program_id: &crate::ID,
accounts: &account_metas,
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) },
};

invoke_signed(
&instruction,
&[self.token, self.mint, self.delegate, self.authority],
signers,
)
}
}
65 changes: 65 additions & 0 deletions programs/token/src/instructions/burn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use core::slice::from_raw_parts;

use pinocchio::{
account_info::AccountInfo,
instruction::{AccountMeta, Instruction, Signer},
program::invoke_signed,
ProgramResult,
};

use crate::{write_bytes, UNINIT_BYTE};

/// Burns tokens by removing them from an account.
///
/// ### Accounts:
/// 0. `[WRITE]` The account to burn from.
/// 1. `[WRITE]` The token mint.
/// 2. `[SIGNER]` The account's owner/delegate.
pub struct Burn<'a> {
/// Source of the Burn Account
pub token: &'a AccountInfo,
/// Mint Account
pub mint: &'a AccountInfo,
/// Owner of the Token Account
pub authority: &'a AccountInfo,
/// Amount
pub amount: u64,
}

impl<'a> Burn<'a> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}

pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
// Account metadata
let account_metas: [AccountMeta; 3] = [
AccountMeta::writable(self.token.key()),
AccountMeta::writable(self.mint.key()),
AccountMeta::readonly_signer(self.authority.key()),
];

// Instruction data
// - [0]: instruction discriminator (1 byte, u8)
// - [1..9]: amount (8 bytes, u64)
let mut instruction_data = [UNINIT_BYTE; 9];

// Set discriminator as u8 at offset [0]
write_bytes(&mut instruction_data, &[8]);
// Set amount as u64 at offset [1..9]
write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes());

let instruction = Instruction {
program_id: &crate::ID,
accounts: &account_metas,
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) },
};

invoke_signed(
&instruction,
&[self.token, self.mint, self.authority],
signers,
)
}
}
69 changes: 69 additions & 0 deletions programs/token/src/instructions/burn_checked.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use core::slice::from_raw_parts;

use crate::{write_bytes, UNINIT_BYTE};
use pinocchio::{
account_info::AccountInfo,
instruction::{AccountMeta, Instruction, Signer},
program::invoke_signed,
ProgramResult,
};

/// Burns tokens by removing them from an account.
///
/// ### Accounts:
/// 0. `[WRITE]` The account to burn from.
/// 1. `[WRITE]` The token mint.
/// 2. `[SIGNER]` The account's owner/delegate.
pub struct BurnChecked<'a> {
/// Source of the Burn Account
pub token: &'a AccountInfo,
/// Mint Account
pub mint: &'a AccountInfo,
/// Owner of the Token Account
pub authority: &'a AccountInfo,
/// Amount
pub amount: u64,
/// Decimals
pub decimals: u8,
}

impl<'a> BurnChecked<'a> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}

pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
// Account metadata
let account_metas: [AccountMeta; 3] = [
AccountMeta::writable(self.token.key()),
AccountMeta::writable(self.mint.key()),
AccountMeta::readonly_signer(self.authority.key()),
];

// Instruction data
// - [0]: instruction discriminator (1 byte, u8)
// - [1..9]: amount (8 bytes, u64)
// - [9]: decimals (1 byte, u8)
let mut instruction_data = [UNINIT_BYTE; 10];

// Set discriminator as u8 at offset [0]
write_bytes(&mut instruction_data, &[15]);
// Set amount as u64 at offset [1..9]
write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes());
// Set decimals as u8 at offset [9]
write_bytes(&mut instruction_data[9..], &[self.decimals]);

let instruction = Instruction {
program_id: &crate::ID,
accounts: &account_metas,
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) },
};

invoke_signed(
&instruction,
&[self.token, self.mint, self.authority],
signers,
)
}
}
49 changes: 49 additions & 0 deletions programs/token/src/instructions/close_account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use pinocchio::{
account_info::AccountInfo,
instruction::{AccountMeta, Instruction, Signer},
program::invoke_signed,
ProgramResult,
};

/// Close an account by transferring all its SOL to the destination account.
///
/// ### Accounts:
/// 0. `[WRITE]` The account to close.
/// 1. `[WRITE]` The destination account.
/// 2. `[SIGNER]` The account's owner.
pub struct CloseAccount<'a> {
/// Token Account.
pub account: &'a AccountInfo,
/// Destination Account
pub destination: &'a AccountInfo,
/// Owner Account
pub authority: &'a AccountInfo,
}

impl<'a> CloseAccount<'a> {
#[inline(always)]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}

pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
// account metadata
let account_metas: [AccountMeta; 3] = [
AccountMeta::writable(self.account.key()),
AccountMeta::writable(self.destination.key()),
AccountMeta::readonly_signer(self.authority.key()),
];

let instruction = Instruction {
program_id: &crate::ID,
accounts: &account_metas,
data: &[9],
};

invoke_signed(
&instruction,
&[self.account, self.destination, self.authority],
signers,
)
}
}
Loading