Skip to content

Commit e2f66e6

Browse files
committed
add token-2022 core ixs and state
1 parent 78a8b44 commit e2f66e6

30 files changed

+1756
-3
lines changed

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"programs/memo",
66
"programs/system",
77
"programs/token",
8+
"programs/token-2022",
89
"sdk/log/crate",
910
"sdk/log/macro",
1011
"sdk/pinocchio",

programs/token-2022/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "pinocchio-token-2022"
3+
description = "Pinocchio helpers to invoke Token program 2022 instructions"
4+
version = "0.1.0"
5+
edition = { workspace = true }
6+
license = { workspace = true }
7+
readme = "./README.md"
8+
repository = { workspace = true }
9+
10+
[lib]
11+
crate-type = ["rlib"]
12+
13+
[dependencies]
14+
pinocchio = { workspace = true }
15+
pinocchio-pubkey = { workspace = true }

programs/token-2022/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<p align="center">
2+
<img alt="pinocchio-token-2022" src="https://github.com/user-attachments/assets/4048fe96-9096-4441-85c3-5deffeb089a6" height="100"/>
3+
</p>
4+
<h3 align="center">
5+
<code>pinocchio-token-2022</code>
6+
</h3>
7+
<p align="center">
8+
<a href="https://crates.io/crates/pinocchio-token-2022"><img src="https://img.shields.io/crates/v/pinocchio-token-2022?logo=rust" /></a>
9+
<a href="https://docs.rs/pinocchio-token-2022"><img src="https://img.shields.io/docsrs/pinocchio-token-2022?logo=docsdotrs" /></a>
10+
</p>
11+
12+
## Overview
13+
14+
This crate contains [`pinocchio`](https://crates.io/crates/pinocchio) helpers to perform cross-program invocations (CPIs) for SPL Token instructions.
15+
16+
Each instruction defines a `struct` with the accounts and parameters required. Once all values are set, you can call directly `invoke` or `invoke_signed` to perform the CPI.
17+
18+
This is a `no_std` crate.
19+
20+
> **Note:** The API defined in this crate is subject to change.
21+
22+
## Examples
23+
24+
Initializing a mint account:
25+
26+
```rust
27+
// This example assumes that the instruction receives a writable `mint`
28+
// account; `authority` is a `Pubkey`.
29+
// Token Program : Legacy Token Program
30+
InitializeMint {
31+
mint,
32+
rent_sysvar,
33+
decimals: 9,
34+
mint_authority: authority,
35+
freeze_authority: Some(authority),
36+
program: Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
37+
}.invoke()?;
38+
```
39+
40+
Performing a transfer of tokens:
41+
42+
```rust
43+
// This example assumes that the instruction receives writable `from` and `to`
44+
// accounts, and a signer `authority` account.
45+
// Token Program : Token program 2022
46+
Transfer {
47+
from,
48+
to,
49+
authority,
50+
amount: 10,
51+
program: Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb")
52+
}.invoke()?;
53+
```
54+
55+
## License
56+
57+
The code is licensed under the [Apache License Version 2.0](../LICENSE)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use core::slice::from_raw_parts;
2+
3+
use pinocchio::{
4+
account_info::AccountInfo,
5+
instruction::{AccountMeta, Instruction, Signer},
6+
program::invoke_signed,
7+
pubkey::Pubkey,
8+
ProgramResult,
9+
};
10+
11+
use crate::{write_bytes, UNINIT_BYTE};
12+
13+
/// Approves a delegate.
14+
///
15+
/// ### Accounts:
16+
/// 0. `[WRITE]` The token account.
17+
/// 1. `[]` The delegate.
18+
/// 2. `[SIGNER]` The source account owner.
19+
pub struct Approve<'a> {
20+
/// Source Account.
21+
pub source: &'a AccountInfo,
22+
/// Delegate Account
23+
pub delegate: &'a AccountInfo,
24+
/// Source Owner Account
25+
pub authority: &'a AccountInfo,
26+
/// Amount
27+
pub amount: u64,
28+
/// Token Program
29+
pub token_program: &'a Pubkey,
30+
}
31+
32+
impl Approve<'_> {
33+
#[inline(always)]
34+
pub fn invoke(&self) -> ProgramResult {
35+
self.invoke_signed(&[])
36+
}
37+
38+
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
39+
// Account metadata
40+
let account_metas: [AccountMeta; 3] = [
41+
AccountMeta::writable(self.source.key()),
42+
AccountMeta::readonly(self.delegate.key()),
43+
AccountMeta::readonly_signer(self.authority.key()),
44+
];
45+
46+
// Instruction data
47+
// - [0]: instruction discriminator (1 byte, u8)
48+
// - [1..9]: amount (8 bytes, u64)
49+
let mut instruction_data = [UNINIT_BYTE; 9];
50+
51+
// Set discriminator as u8 at offset [0]
52+
write_bytes(&mut instruction_data, &[4]);
53+
// Set amount as u64 at offset [1..9]
54+
write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes());
55+
56+
let instruction = Instruction {
57+
program_id: self.token_program,
58+
accounts: &account_metas,
59+
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) },
60+
};
61+
62+
invoke_signed(
63+
&instruction,
64+
&[self.source, self.delegate, self.authority],
65+
signers,
66+
)
67+
}
68+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use core::slice::from_raw_parts;
2+
3+
use pinocchio::{
4+
account_info::AccountInfo,
5+
instruction::{AccountMeta, Instruction, Signer},
6+
program::invoke_signed,
7+
pubkey::Pubkey,
8+
ProgramResult,
9+
};
10+
11+
use crate::{write_bytes, UNINIT_BYTE};
12+
13+
/// Approves a delegate.
14+
///
15+
/// ### Accounts:
16+
/// 0. `[WRITE]` The source account.
17+
/// 1. `[]` The token mint.
18+
/// 2. `[]` The delegate.
19+
/// 3. `[SIGNER]` The source account owner.
20+
pub struct ApproveChecked<'a> {
21+
/// Source Account.
22+
pub source: &'a AccountInfo,
23+
/// Mint Account.
24+
pub mint: &'a AccountInfo,
25+
/// Delegate Account.
26+
pub delegate: &'a AccountInfo,
27+
/// Source Owner Account.
28+
pub authority: &'a AccountInfo,
29+
/// Amount.
30+
pub amount: u64,
31+
/// Decimals.
32+
pub decimals: u8,
33+
/// Token Program
34+
pub token_program: &'a Pubkey,
35+
}
36+
37+
impl ApproveChecked<'_> {
38+
#[inline(always)]
39+
pub fn invoke(&self) -> ProgramResult {
40+
self.invoke_signed(&[])
41+
}
42+
43+
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
44+
// Account metadata
45+
let account_metas: [AccountMeta; 4] = [
46+
AccountMeta::writable(self.source.key()),
47+
AccountMeta::readonly(self.mint.key()),
48+
AccountMeta::readonly(self.delegate.key()),
49+
AccountMeta::readonly_signer(self.authority.key()),
50+
];
51+
52+
// Instruction data
53+
// - [0] : instruction discriminator (1 byte, u8)
54+
// - [1..9]: amount (8 bytes, u64)
55+
// - [9] : decimals (1 byte, u8)
56+
let mut instruction_data = [UNINIT_BYTE; 10];
57+
58+
// Set discriminator as u8 at offset [0]
59+
write_bytes(&mut instruction_data, &[13]);
60+
// Set amount as u64 at offset [1..9]
61+
write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes());
62+
// Set decimals as u8 at offset [9]
63+
write_bytes(&mut instruction_data[9..], &[self.decimals]);
64+
65+
let instruction = Instruction {
66+
program_id: self.token_program,
67+
accounts: &account_metas,
68+
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) },
69+
};
70+
71+
invoke_signed(
72+
&instruction,
73+
&[self.source, self.mint, self.delegate, self.authority],
74+
signers,
75+
)
76+
}
77+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use core::slice::from_raw_parts;
2+
3+
use pinocchio::{
4+
account_info::AccountInfo,
5+
instruction::{AccountMeta, Instruction, Signer},
6+
program::invoke_signed,
7+
pubkey::Pubkey,
8+
ProgramResult,
9+
};
10+
11+
use crate::{write_bytes, UNINIT_BYTE};
12+
13+
/// Burns tokens by removing them from an account.
14+
///
15+
/// ### Accounts:
16+
/// 0. `[WRITE]` The account to burn from.
17+
/// 1. `[WRITE]` The token mint.
18+
/// 2. `[SIGNER]` The account's owner/delegate.
19+
pub struct Burn<'a> {
20+
/// Source of the Burn Account
21+
pub account: &'a AccountInfo,
22+
/// Mint Account
23+
pub mint: &'a AccountInfo,
24+
/// Owner of the Token Account
25+
pub authority: &'a AccountInfo,
26+
/// Amount
27+
pub amount: u64,
28+
/// Token Program
29+
pub token_program: &'a Pubkey,
30+
}
31+
32+
impl Burn<'_> {
33+
#[inline(always)]
34+
pub fn invoke(&self) -> ProgramResult {
35+
self.invoke_signed(&[])
36+
}
37+
38+
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
39+
// Account metadata
40+
let account_metas: [AccountMeta; 3] = [
41+
AccountMeta::writable(self.account.key()),
42+
AccountMeta::writable(self.mint.key()),
43+
AccountMeta::readonly_signer(self.authority.key()),
44+
];
45+
46+
// Instruction data
47+
// - [0]: instruction discriminator (1 byte, u8)
48+
// - [1..9]: amount (8 bytes, u64)
49+
let mut instruction_data = [UNINIT_BYTE; 9];
50+
51+
// Set discriminator as u8 at offset [0]
52+
write_bytes(&mut instruction_data, &[8]);
53+
// Set amount as u64 at offset [1..9]
54+
write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes());
55+
56+
let instruction = Instruction {
57+
program_id: self.token_program,
58+
accounts: &account_metas,
59+
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) },
60+
};
61+
62+
invoke_signed(
63+
&instruction,
64+
&[self.account, self.mint, self.authority],
65+
signers,
66+
)
67+
}
68+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use core::slice::from_raw_parts;
2+
3+
use crate::{write_bytes, UNINIT_BYTE};
4+
use pinocchio::{
5+
account_info::AccountInfo,
6+
instruction::{AccountMeta, Instruction, Signer},
7+
program::invoke_signed,
8+
pubkey::Pubkey,
9+
ProgramResult,
10+
};
11+
12+
/// Burns tokens by removing them from an account.
13+
///
14+
/// ### Accounts:
15+
/// 0. `[WRITE]` The account to burn from.
16+
/// 1. `[WRITE]` The token mint.
17+
/// 2. `[SIGNER]` The account's owner/delegate.
18+
pub struct BurnChecked<'a> {
19+
/// Source of the Burn Account
20+
pub account: &'a AccountInfo,
21+
/// Mint Account
22+
pub mint: &'a AccountInfo,
23+
/// Owner of the Token Account
24+
pub authority: &'a AccountInfo,
25+
/// Amount
26+
pub amount: u64,
27+
/// Decimals
28+
pub decimals: u8,
29+
/// Token Program
30+
pub token_program: &'a Pubkey,
31+
}
32+
33+
impl BurnChecked<'_> {
34+
#[inline(always)]
35+
pub fn invoke(&self) -> ProgramResult {
36+
self.invoke_signed(&[])
37+
}
38+
39+
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
40+
// Account metadata
41+
let account_metas: [AccountMeta; 3] = [
42+
AccountMeta::writable(self.account.key()),
43+
AccountMeta::writable(self.mint.key()),
44+
AccountMeta::readonly_signer(self.authority.key()),
45+
];
46+
47+
// Instruction data
48+
// - [0]: instruction discriminator (1 byte, u8)
49+
// - [1..9]: amount (8 bytes, u64)
50+
// - [9]: decimals (1 byte, u8)
51+
let mut instruction_data = [UNINIT_BYTE; 10];
52+
53+
// Set discriminator as u8 at offset [0]
54+
write_bytes(&mut instruction_data, &[15]);
55+
// Set amount as u64 at offset [1..9]
56+
write_bytes(&mut instruction_data[1..9], &self.amount.to_le_bytes());
57+
// Set decimals as u8 at offset [9]
58+
write_bytes(&mut instruction_data[9..], &[self.decimals]);
59+
60+
let instruction = Instruction {
61+
program_id: self.token_program,
62+
accounts: &account_metas,
63+
data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 10) },
64+
};
65+
66+
invoke_signed(
67+
&instruction,
68+
&[self.account, self.mint, self.authority],
69+
signers,
70+
)
71+
}
72+
}

0 commit comments

Comments
 (0)