-
Notifications
You must be signed in to change notification settings - Fork 1
sBPF assembly example
License
firedancer-io/token.sbpf
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
# TOKEN.SBPF version 0.99 #
# Simple token transfer program #
# #
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::#
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::#
#: o 8 .oPYo. .oPYo. ooooo :#
#: 8 8 8 `8 8 8 8 :#
#: o8P .oPYo. 8 .o .oPYo. odYo. .oPYo. o8YooP' o8YooP' o8oo :#
#: 8 8 8 8oP' 8oooo8 8' `8 Yb.. 8 `b 8 8 :#
#: 8 8 8 8 `b. 8. 8 8 /--\ 'Yb. 8 8 8 8 :#
#: 8 `YooP' 8 `o. `Yooo' 8 8 \--/ `YooP' 8oooP' 8 8 :#
#:::..::.....:..::...:.....:..::..::..:::.....::......::..::::::..:::::#
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::#
# #
# #
# token.sBPF is a CU-minimized token program for Solana. #
# It is functionally comparable to Tokenkeg (signed minting, #
# permission-less transfers), but does not support freezing. #
# #
# Notably, "express" token transfers cost only 56 CUs. #
# #
# #
#----------------------------------------------------------------------#
# #
# token.sBPF is distributed WITHOUT WARRANTIES OF ANY KIND. #
# This code has not been audited. Please assume it is broken. #
# #
# DO NOT DEPLOY THIS CODE ON MAINNET. #
# #
# For more information refer to the LICENSE file. #
# #
#----------------------------------------------------------------------#
# #
# Build: #
# #
# Set $SOLANA_SDK to a Solana platform-tools installation #
# Run `make` #
# The resulting program is at BUILD/TOKEN.SO #
# #
# This program is compatible with BPF Loader v3 (sBPFv1). #
# #
# Usage: #
# #
# This program manages two kinds of accounts: #
# A mint whose account address identifies the token. The mint is #
# a required signatory when issuing new tokens. #
# Token accounts which collectively hold the supply of a token. #
# #
# Instructions: #
# #
# Mint Tokens #
# - Account 0: Mint ; signer writable sz=0x08 #
# - Account 1: Token account ; writable sz=0x48 #
# - Data (9 bytes): #
# - u8 op_kind = 0 #
# - u64 amount #
# #
# Set Authority / Set Mint #
# - Account 0: Token account ; writable sz=0x48 #
# - Account 1: Authority ; signer #
# - Data (33 bytes): #
# - u8 op_kind = 1 #
# - 32b new_authority #
# - Data (65 bytes): #
# - u8 op_kind = 1 #
# - 32b new_authority #
# - 32b mint_address #
# #
# Transfer #
# - Account 0: Src token account ; writable sz=0x48 #
# - Account 1: Dst token account ; writable sz=0x48 #
# - Account 2: Authority of src ; signer #
# - Data (8 bytes): #
# - u64 amount #
# - Uses 56 CUs if all addresses are unique, token accounts are #
# initialized and same mint, authority is correct, and no #
# balance underflow or overflow. #
# #
# State: #
# #
# Mint (8 bytes) #
# - u64 total_minted #
# #
# Token Account (72 bytes) #
# - 32b mint address #
# - 32b token account address #
# - u64 balance #
# #
#----------------------------------------------------------------------#
# Imports
.extern sol_log_
.extern entrypoint_ex
# Code
.section .text
#define TOKEN_ACCT_SZ 0x48
/* Various offsets derived using offsets.py */
#define INOFF_ACCT_CNT 0x0000
#define INOFF_ACCT0_HDR 0x0008
#define INOFF_ACCT0_SZ 0x0058
#define INOFF_ACCT0_MINT_Q0 0x0060
#define INOFF_ACCT0_MINT_Q1 0x0068
#define INOFF_ACCT0_MINT_Q2 0x0070
#define INOFF_ACCT0_MINT_Q3 0x0078
#define INOFF_ACCT0_AUTH_Q0 0x0080
#define INOFF_ACCT0_AUTH_Q1 0x0088
#define INOFF_ACCT0_AUTH_Q2 0x0090
#define INOFF_ACCT0_AUTH_Q3 0x0098
#define INOFF_ACCT0_BALANCE 0x00a0
#define INOFF_ACCT1_HDR 0x28b0
#define INOFF_ACCT1_SZ 0x2900
#define INOFF_ACCT1_MINT_Q0 0x2908
#define INOFF_ACCT1_MINT_Q1 0x2910
#define INOFF_ACCT1_MINT_Q2 0x2918
#define INOFF_ACCT1_MINT_Q3 0x2920
#define INOFF_ACCT1_AUTH_Q0 0x2928
#define INOFF_ACCT1_AUTH_Q1 0x2930
#define INOFF_ACCT1_AUTH_Q2 0x2938
#define INOFF_ACCT1_AUTH_Q3 0x2940
#define INOFF_ACCT1_BALANCE 0x2948
#define INOFF_ACCT2_HDR 0x5158
#define INOFF_ACCT2_ADDR_Q0 0x5160
#define INOFF_ACCT2_ADDR_Q1 0x5168
#define INOFF_ACCT2_ADDR_Q2 0x5170
#define INOFF_ACCT2_ADDR_Q3 0x5178
#define MIN_INSTR_DATA_OFF 0x79b8
.globl entrypoint
entrypoint:
# check: account_cnt == 3
r3 = *(u64 *)(r1 + INOFF_ACCT_CNT) # 1 CU
if r3 != 3 goto beach # 2 CU
# check: account[0].duplicate_index == 0xFF &&
# account[0].is_signer == 0x01 &&
# account[0].is_writable == 0x01 &&
# account[0].is_executable == 0x00
r3 = *(u32 *)(r1 + INOFF_ACCT0_HDR) # 3 CU
r3 &= 0xFF00FF # 4 CU
if r3 != 0x0100FF goto beach # 5 CU
# check: account[0].size == TOKEN_ACCT_SZ
r3 = *(u64 *)(r1 + INOFF_ACCT0_SZ) # 6 CU
if r3 != TOKEN_ACCT_SZ goto beach # 7 CU
# check: account[1].duplicate_index == 0xFF &&
# account[1].is_writable == 0x01 &&
# account[1].is_executable == 0x00
#
# Since the account is writable omit the owner check (dangerous!)
r3 = *(u32 *)(r1 + INOFF_ACCT1_HDR) # 8 CU
r3 &= 0xFF00FF # 9 CU
if r3 != 0x0100FF goto beach # 10 CU
# check: account[1].size == TOKEN_ACCT_SZ
r3 = *(u64 *)(r1 + INOFF_ACCT1_SZ) # 11 CU
if r3 != TOKEN_ACCT_SZ goto beach # 12 CU
# check: account[2].duplicate_index == 0xFF &&
# account[2].is_signer == 0x01
r3 = *(u16 *)(r1 + INOFF_ACCT2_HDR) # 13 CU
if r3 != 0x01FF goto beach # 14 CU
# check: account[0].data.mint == account[1].data.mint
r4 = *(u64 *)(r1 + INOFF_ACCT0_MINT_Q0) # 15 CU
r5 = *(u64 *)(r1 + INOFF_ACCT0_MINT_Q1) # 16 CU
r6 = *(u64 *)(r1 + INOFF_ACCT1_MINT_Q0) # 17 CU
r7 = *(u64 *)(r1 + INOFF_ACCT1_MINT_Q1) # 18 CU
if r4 != r6 goto beach # 19 CU
if r5 != r7 goto beach # 20 CU
r4 = *(u64 *)(r1 + INOFF_ACCT0_MINT_Q2) # 21 CU
r5 = *(u64 *)(r1 + INOFF_ACCT0_MINT_Q3) # 22 CU
r6 = *(u64 *)(r1 + INOFF_ACCT1_MINT_Q2) # 23 CU
r7 = *(u64 *)(r1 + INOFF_ACCT1_MINT_Q3) # 24 CU
if r4 != r6 goto beach # 25 CU
if r5 != r7 goto beach # 26 CU
# check: account[0].data.authority == account[2].pubkey
r4 = *(u64 *)(r1 + INOFF_ACCT0_AUTH_Q0) # 27 CU
r5 = *(u64 *)(r1 + INOFF_ACCT0_AUTH_Q1) # 28 CU
r6 = *(u64 *)(r1 + INOFF_ACCT2_ADDR_Q0) # 29 CU
r7 = *(u64 *)(r1 + INOFF_ACCT2_ADDR_Q1) # 30 CU
if r4 != r6 goto beach # 31 CU
if r5 != r7 goto beach # 32 CU
r4 = *(u64 *)(r1 + INOFF_ACCT0_AUTH_Q2) # 33 CU
r5 = *(u64 *)(r1 + INOFF_ACCT0_AUTH_Q3) # 34 CU
r6 = *(u64 *)(r1 + INOFF_ACCT2_ADDR_Q2) # 35 CU
r7 = *(u64 *)(r1 + INOFF_ACCT2_ADDR_Q3) # 36 CU
if r4 != r6 goto beach # 37 CU
if r5 != r7 goto beach # 38 CU
# r3 <- align_up( account[2].size, 8 )
r3 = *(u64 *)(r1 + 0x5198) # 39 CU
r3 += 7 # 40 CU
r3 &= -8 # 41 CU
# r3 <- &instr.data
r3 += r1 # 42 CU
r3 += MIN_INSTR_DATA_OFF # 43 CU
# check: instr.data_sz == 0x08
r2 = *(u64 *)(r3 + 0x00) # 44 CU
if r2 != 0x08 goto beach # 45 CU
# r2 <- instr.data.amount
# r3 <- account[0].data.balance
# r4 <- account[1].data.balance
r2 = *(u64 *)(r3 + 0x08) # 46 CU
r3 = *(u64 *)(r1 + INOFF_ACCT0_BALANCE) # 47 CU
r4 = *(u64 *)(r1 + INOFF_ACCT1_BALANCE) # 48 CU
# check: instr.data.amount <= account[0].data.balance
if r2 > r3 goto insufficient_balance # 49 CU
# account[0].data.balance -= instr.data.amount
r3 -= r2 # 50 CU
*(u64 *)(r1 + INOFF_ACCT0_BALANCE) = r3 # 51 CU
# check: account[1].data.balance + instr.data.amount < 2^64
# account[1].data.balance += instr.data.amount
r5 = r4 # 52 CU
r5 += r2 # 53 CU
if r5 < r4 goto overflow # 54 CU
*(u64 *)(r1 + INOFF_ACCT1_BALANCE) = r5 # 55 CU
# Token transfer complete
exit # 56 CU
insufficient_balance:
r1 = str_insufficient_balance ll
r2 = 20
call sol_log_
r0 = 2
exit
overflow:
r1 = str_overflow ll
r2 = 9
call sol_log
r0 = 3
exit
# Trampoline to slow path
beach:
r1 = 0x400000000 ll
call entrypoint_ex
exit
#----------------------------------------------------------------------#
# Note on known issues: #
# #
# - The mint ID is 32 bytes but could be shortened to 8 bytes by #
# using PDAs for mint addresses. This would save approx 11 CU #
# for transfers. #
# #
# - The program does not check whether provided accounts are owned #
# by itself. Instead it checks whether an account is writable. #
# This may be safe for now but could be exploitable (infinite #
# mint bug) if network allows programs to write to accounts other #
# than itself in the future. #
# #
# - No support for closing accounts yet. #
# #
# - Incomplete tests. #
# #
#----------------------------------------------------------------------#
About
sBPF assembly example