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

Commit 3aa4dad

Browse files
committed
Switch to no_std
1 parent 7ade28e commit 3aa4dad

File tree

7 files changed

+92
-44
lines changed

7 files changed

+92
-44
lines changed

Cargo.lock

Lines changed: 18 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
@@ -16,4 +16,5 @@ repository = "https://github.com/febo/token"
1616

1717
[workspace.dependencies]
1818
pinocchio = { git = "https://github.com/febo/pinocchio.git", branch = "febo/log-arguments" }
19+
pinocchio-log = { git = "https://github.com/febo/pinocchio.git", branch = "febo/log-arguments" }
1920
pinocchio-pubkey = { git = "https://github.com/febo/pinocchio.git", branch = "febo/log-arguments" }

program/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ test-sbf = []
1919

2020
[dependencies]
2121
pinocchio = { workspace = true }
22+
pinocchio-log = { workspace = true }
2223
pinocchio-pubkey = { workspace = true }
2324
token-interface = { version = "^0", path = "../interface" }
2425

program/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//! An ERC20-like Token program for the Solana blockchain.
1+
//! Another ERC20-like Token program for the Solana blockchain.
2+
3+
#![no_std]
24

35
mod entrypoint;
46
mod processor;

program/src/processor/amount_to_ui_amount.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use core::str::from_utf8_unchecked;
12
use pinocchio::{
23
account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult,
34
};
5+
use pinocchio_log::logger::{Argument, Logger};
46
use token_interface::state::mint::Mint;
57

6-
use super::{amount_to_ui_amount_string_trimmed, check_account_owner};
8+
use super::{check_account_owner, MAX_DIGITS_U64};
79

810
#[inline(always)]
911
pub fn process_amount_to_ui_amount(
@@ -21,8 +23,17 @@ pub fn process_amount_to_ui_amount(
2123

2224
let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
2325

24-
let ui_amount = amount_to_ui_amount_string_trimmed(amount, mint.decimals);
25-
set_return_data(&ui_amount.into_bytes());
26+
let mut logger = Logger::<MAX_DIGITS_U64>::default();
27+
logger.append_with_args(amount, &[Argument::Precision(mint.decimals)]);
28+
29+
let mut s = unsafe { from_utf8_unchecked(&logger) };
30+
31+
if mint.decimals > 0 {
32+
let zeros_trimmed = s.trim_end_matches('0');
33+
s = zeros_trimmed.trim_end_matches('.');
34+
}
35+
36+
set_return_data(s.as_bytes());
2637

2738
Ok(())
2839
}

program/src/processor/mod.rs

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use core::{mem::MaybeUninit, slice::from_raw_parts, str::from_utf8_unchecked};
12
use pinocchio::{
2-
account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
3+
account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, syscalls::sol_memcpy_,
4+
ProgramResult,
35
};
46
use token_interface::{
57
error::TokenError,
@@ -67,6 +69,12 @@ const INCINERATOR_ID: Pubkey =
6769
/// System program id.
6870
const SYSTEM_PROGRAM_ID: Pubkey = pinocchio_pubkey::pubkey!("11111111111111111111111111111111");
6971

72+
/// An uninitialized byte.
73+
const UNINIT_BYTE: MaybeUninit<u8> = MaybeUninit::uninit();
74+
75+
/// Maximum number of digits in a `u64``.
76+
const MAX_DIGITS_U64: usize = 20;
77+
7078
#[inline(always)]
7179
fn is_owned_by_system_program_or_incinerator(owner: &Pubkey) -> bool {
7280
&SYSTEM_PROGRAM_ID == owner || &INCINERATOR_ID == owner
@@ -120,56 +128,62 @@ fn validate_owner(
120128
Ok(())
121129
}
122130

123-
/// Convert a raw amount to its UI representation using the given decimals field
124-
/// Excess zeroes or unneeded decimal point are trimmed.
125-
#[inline(always)]
126-
fn amount_to_ui_amount_string_trimmed(amount: u64, decimals: u8) -> String {
127-
let mut s = amount_to_ui_amount_string(amount, decimals);
128-
if decimals > 0 {
129-
let zeros_trimmed = s.trim_end_matches('0');
130-
s = zeros_trimmed.trim_end_matches('.').to_string();
131-
}
132-
s
133-
}
134-
135-
/// Convert a raw amount to its UI representation (using the decimals field
136-
/// defined in its mint)
137-
#[inline(always)]
138-
fn amount_to_ui_amount_string(amount: u64, decimals: u8) -> String {
139-
let decimals = decimals as usize;
140-
if decimals > 0 {
141-
// Left-pad zeros to decimals + 1, so we at least have an integer zero
142-
let mut s = format!("{:01$}", amount, decimals + 1);
143-
// Add the decimal point (Sorry, "," locales!)
144-
s.insert(s.len() - decimals, '.');
145-
s
146-
} else {
147-
amount.to_string()
148-
}
149-
}
150-
151131
/// Try to convert a UI representation of a token amount to its raw amount using
152132
/// the given decimals field
153-
fn try_ui_amount_into_amount(ui_amount: String, decimals: u8) -> Result<u64, ProgramError> {
133+
fn try_ui_amount_into_amount(ui_amount: &str, decimals: u8) -> Result<u64, ProgramError> {
154134
let decimals = decimals as usize;
155135
let mut parts = ui_amount.split('.');
136+
156137
// splitting a string, even an empty one, will always yield an iterator of at
157138
// least length == 1
158-
let mut amount_str = parts.next().unwrap().to_string();
139+
let amount_str = parts.next().unwrap();
140+
let mut length = amount_str.len();
141+
142+
let mut digits = [UNINIT_BYTE; MAX_DIGITS_U64];
143+
let mut ptr = digits.as_mut_ptr();
144+
145+
unsafe {
146+
sol_memcpy_(
147+
ptr as *mut _,
148+
amount_str.as_ptr() as *const _,
149+
length as u64,
150+
);
151+
}
152+
159153
let after_decimal = parts.next().unwrap_or("");
160154
let after_decimal = after_decimal.trim_end_matches('0');
155+
161156
if (amount_str.is_empty() && after_decimal.is_empty())
162157
|| parts.next().is_some()
163158
|| after_decimal.len() > decimals
164159
{
165160
return Err(ProgramError::InvalidArgument);
166161
}
167162

168-
amount_str.push_str(after_decimal);
169-
for _ in 0..decimals.saturating_sub(after_decimal.len()) {
170-
amount_str.push('0');
163+
unsafe {
164+
sol_memcpy_(
165+
ptr.add(length) as *mut _,
166+
after_decimal.as_ptr() as *const _,
167+
after_decimal.len() as u64,
168+
);
169+
170+
length += after_decimal.len();
171+
ptr = ptr.add(length);
172+
}
173+
174+
let remaining = decimals.saturating_sub(after_decimal.len());
175+
176+
for offset in 0..remaining {
177+
unsafe {
178+
(ptr.add(offset) as *mut u8).write(b'0');
179+
}
180+
}
181+
182+
length += remaining;
183+
184+
unsafe {
185+
from_utf8_unchecked(from_raw_parts(digits.as_ptr() as _, length))
186+
.parse::<u64>()
187+
.map_err(|_| ProgramError::InvalidArgument)
171188
}
172-
amount_str
173-
.parse::<u64>()
174-
.map_err(|_| ProgramError::InvalidArgument)
175189
}

program/src/processor/ui_amount_to_amount.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use core::str::from_utf8;
12
use pinocchio::{
23
account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult,
34
};
@@ -10,15 +11,15 @@ pub fn process_ui_amount_to_amount(
1011
accounts: &[AccountInfo],
1112
instruction_data: &[u8],
1213
) -> ProgramResult {
13-
let ui_amount = core::str::from_utf8(instruction_data)
14-
.map_err(|_error| ProgramError::InvalidInstructionData)?;
14+
let ui_amount =
15+
from_utf8(instruction_data).map_err(|_error| ProgramError::InvalidInstructionData)?;
1516

1617
let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
1718
check_account_owner(mint_info)?;
1819

1920
let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
2021

21-
let amount = try_ui_amount_into_amount(ui_amount.to_string(), mint.decimals)?;
22+
let amount = try_ui_amount_into_amount(ui_amount, mint.decimals)?;
2223
set_return_data(&amount.to_le_bytes());
2324

2425
Ok(())

0 commit comments

Comments
 (0)