//! NEP-20 Token Standard - Production Implementation
#![no_std]
extern crate alloc;
use alloc::string::String;
use nexus_sdk::{
solidity::{*, SafeMath},
contract_api::{ez::prelude::*, ez::ret},
require,
};
use nexus_sdk::solidity::uint256 as U256;
// State Variables
pub static BALANCES: Mapping<Address, U256> = Mapping::new(b"bal");
pub static ALLOWANCES: DoubleMapping<Address, Address, U256> = DoubleMapping::new(b"allow");
pub static TOTAL_SUPPLY: Mapping<&[u8], U256> = Mapping::new(b"supply");
pub static OWNER: Mapping<&[u8], Address> = Mapping::new(b"owner");
pub static INITIALIZED: Mapping<&[u8], bool> = Mapping::new(b"init");
// Constants
pub const NAME_KEY: &[u8] = b"name";
pub const SYMBOL_KEY: &[u8] = b"symbol";
pub const DECIMALS_KEY: &[u8] = b"decimals";
/// Internal module containing reusable logic
pub mod internal {
use super::*;
/// Manual U256 serialization to avoid WASM intrinsics
pub fn u256_to_bytes(value: U256) -> [u8; 32] {
value.to_little_endian()
}
/// Returns true if a < b
pub fn u256_lt(a: &U256, b: &U256) -> bool {
if a.0[3] < b.0[3] { return true; }
if a.0[3] > b.0[3] { return false; }
if a.0[2] < b.0[2] { return true; }
if a.0[2] > b.0[2] { return false; }
if a.0[1] < b.0[1] { return true; }
if a.0[1] > b.0[1] { return false; }
if a.0[0] < b.0[0] { return true; }
false
}
/// Returns true if a > b
pub fn u256_gt(a: &U256, b: &U256) -> bool {
if a.0[3] > b.0[3] { return true; }
if a.0[3] < b.0[3] { return false; }
if a.0[2] > b.0[2] { return true; }
if a.0[2] < b.0[2] { return false; }
if a.0[1] > b.0[1] { return true; }
if a.0[1] < b.0[1] { return false; }
if a.0[0] > b.0[0] { return true; }
false
}
/// Returns true if a == b
pub fn u256_eq(a: &U256, b: &U256) -> bool {
for i in 0..4 {
if a.0[i] != b.0[i] { return false; }
}
true
}
/// Returns true if a != b
pub fn u256_ne(a: &U256, b: &U256) -> bool {
!u256_eq(a, b)
}
/// Manual U256 addition — no WASM compiler intrinsics
pub fn u256_add(a: &U256, b: &U256) -> U256 {
let mut result = U256::zero();
let mut carry: u64 = 0;
let sum0 = a.0[0].wrapping_add(b.0[0]);
let c0 = if sum0 < a.0[0] { 1u64 } else { 0u64 };
let sum0_c = sum0.wrapping_add(carry);
let c0_2 = if sum0_c < sum0 { 1u64 } else { 0u64 };
result.0[0] = sum0_c;
carry = c0 + c0_2;
let sum1 = a.0[1].wrapping_add(b.0[1]);
let c1 = if sum1 < a.0[1] { 1u64 } else { 0u64 };
let sum1_c = sum1.wrapping_add(carry);
let c1_2 = if sum1_c < sum1 { 1u64 } else { 0u64 };
result.0[1] = sum1_c;
carry = c1 + c1_2;
let sum2 = a.0[2].wrapping_add(b.0[2]);
let c2 = if sum2 < a.0[2] { 1u64 } else { 0u64 };
let sum2_c = sum2.wrapping_add(carry);
let c2_2 = if sum2_c < sum2 { 1u64 } else { 0u64 };
result.0[2] = sum2_c;
carry = c2 + c2_2;
let sum3 = a.0[3].wrapping_add(b.0[3]);
let c3 = if sum3 < a.0[3] { 1u64 } else { 0u64 };
let sum3_c = sum3.wrapping_add(carry);
let c3_2 = if sum3_c < sum3 { 1u64 } else { 0u64 };
result.0[3] = sum3_c;
carry = c3 + c3_2;
let _ = carry;
result
}
/// Checked U256 addition — returns None on overflow
#[inline]
pub fn u256_checked_add(a: &U256, b: &U256) -> Option<U256> {
let result = u256_add(a, b);
if u256_lt(&result, a) || u256_lt(&result, b) {
None
} else {
Some(result)
}
}
/// Manual U256 subtraction — no WASM compiler intrinsics
pub fn u256_sub(a: &U256, b: &U256) -> U256 {
let mut result = U256::zero();
let mut borrow = 0u64;
for i in 0..4 {
let diff = a.0[i].wrapping_sub(b.0[i]).wrapping_sub(borrow);
result.0[i] = diff;
borrow = if (a.0[i] < b.0[i]) || (diff > a.0[i]) { 1 } else { 0 };
}
result
}
pub fn init(name: String, symbol: String, decimals: u8, supply: U256) {
nexus_sdk::contract_api::storage::set(NAME_KEY, name.as_bytes());
nexus_sdk::contract_api::storage::set(SYMBOL_KEY, symbol.as_bytes());
nexus_sdk::contract_api::storage::set(DECIMALS_KEY, &[decimals]);
let sender = Blockchain::msg.sender();
OWNER.set(&b"val".as_slice(), sender.clone());
if u256_gt(&supply, &U256::zero()) {
mint(sender, supply);
}
}
pub fn mint(to: Address, amount: U256) {
let current_supply = TOTAL_SUPPLY.get(&b"total".as_slice());
let new_supply = u256_checked_add(¤t_supply, &amount)
.expect("Mint: total supply overflow");
TOTAL_SUPPLY.set(&b"total".as_slice(), new_supply);
let current_bal = BALANCES.get(&to);
let new_bal = u256_checked_add(¤t_bal, &amount)
.expect("Mint: balance overflow");
BALANCES.set(&to, new_bal);
emit("Transfer", &[]);
}
nexus_fn! {
fn burn(from: Address, amount: U256) {
let sender = Blockchain::msg.sender();
let owner = OWNER.get(&b"val".as_slice());
require!(sender == owner, "Not owner");
let bal = BALANCES.get(&from);
require!(!internal::u256_lt(&bal, &amount), "Burn exceeds balance");
BALANCES.set(&from, internal::u256_sub(&bal, &amount));
let supply = TOTAL_SUPPLY.get(&b"total".as_slice());
require!(!internal::u256_lt(&supply, &amount), "Burn exceeds supply");
TOTAL_SUPPLY.set(&b"total".as_slice(), internal::u256_sub(&supply, &amount));
emit("Transfer", &[]);
ret::u32(1)
}
}
pub fn transfer(sender: Address, to: Address, amount: U256) -> bool {
if to.is_zero() { return false; }
if amount == U256::zero() { return false; }
let sender_bal = BALANCES.get(&sender);
if u256_lt(&sender_bal, &amount) { return false; }
BALANCES.set(&sender, u256_sub(&sender_bal, &amount));
let to_bal = BALANCES.get(&to);
BALANCES.set(&to, u256_add(&to_bal, &amount));
emit("Transfer", &[]);
true
}
pub fn approve(owner: Address, spender: Address, amount: U256) {
ALLOWANCES.set(&owner, &spender, amount);
emit("Approval", &[]);
}
pub fn transfer_from(spender: Address, from: Address, to: Address, amount: U256) -> bool {
if amount == U256::zero() { return false; }
let allowed = ALLOWANCES.get(&from, &spender);
if u256_lt(&allowed, &amount) { return false; }
// Reduce allowance unless it's a max (unlimited) approval
if u256_ne(&allowed, &U256::max_value()) {
ALLOWANCES.set(&from, &spender, u256_sub(&allowed, &amount));
}
internal::transfer(from, to, amount)
}
pub fn balance_of(addr: Address) -> U256 {
BALANCES.get(&addr)
}
}
// ABI Entry Points
nexus_fn! {
fn init_token(name: String, symbol: String, decimals: u64, supply: U256) {
let is_initialized = INITIALIZED.get(&b"val".as_slice());
require!(!is_initialized, "AI"); // Already Initialized
INITIALIZED.set(&b"val".as_slice(), true);
internal::init(name, symbol, decimals as u8, supply);
emit("TokenInitialized", &[]);
ret::u32(1)
}
}
nexus_fn! {
fn mint(to: Address, amount: U256) {
let sender = Blockchain::msg.sender();
let owner = OWNER.get(&b"val".as_slice());
require!(sender == owner, "Not owner");
internal::mint(to, amount);
ret::u32(1)
}
}
nexus_fn! {
fn transferOwnership(new_owner: Address) {
let sender = Blockchain::msg.sender();
let owner = OWNER.get(&b"val".as_slice());
require!(sender == owner, "Not owner");
require!(!new_owner.is_zero(), "Zero address");
OWNER.set(&b"val".as_slice(), new_owner.clone());
emit("OwnershipTransferred", &new_owner.0);
ret::u32(1)
}
}
nexus_fn! {
fn renounceOwnership() {
let sender = Blockchain::msg.sender();
let owner = OWNER.get(&b"val".as_slice());
require!(sender == owner, "Not owner");
OWNER.set(&b"val".as_slice(), Address::zero());
emit("OwnershipRenounced", &[]);
ret::u32(1)
}
}
nexus_fn! {
fn owner() {
let owner = OWNER.get(&b"val".as_slice());
ret::bytes(&owner.0)
}
}
nexus_fn! {
fn balanceOf(addr: Address) {
let bal = internal::balance_of(addr);
let out = internal::u256_to_bytes(bal);
ret::u256(&out)
}
}
nexus_fn! {
fn totalSupply() {
let supply = TOTAL_SUPPLY.get(&b"total".as_slice());
let out = internal::u256_to_bytes(supply);
ret::u256(&out)
}
}
nexus_fn! {
fn name() {
let name_bytes = nexus_sdk::contract_api::storage::get(NAME_KEY);
ret::bytes(&name_bytes)
}
}
nexus_fn! {
fn symbol() {
let symbol_bytes = nexus_sdk::contract_api::storage::get(SYMBOL_KEY);
ret::bytes(&symbol_bytes)
}
}
nexus_fn! {
fn decimals() {
let dec_bytes = nexus_sdk::contract_api::storage::get(DECIMALS_KEY);
let decimals = if dec_bytes.is_empty() { 18u8 } else { dec_bytes[0] };
ret::u32(decimals as u32)
}
}
nexus_fn! {
fn transfer(to: Address, amount: U256) {
let _guard = ReentrancyGuard::new();
let sender = Blockchain::msg.sender();
let success = internal::transfer(sender, to, amount);
if success { ret::u32(1) } else { ret::u32(0) }
}
}
nexus_fn! {
fn approve(spender: Address, amount: U256) {
let sender = Blockchain::msg.sender();
internal::approve(sender, spender, amount);
ret::u32(1)
}
}
nexus_fn! {
fn increaseAllowance(spender: Address, added_value: U256) {
let sender = Blockchain::msg.sender();
let current = ALLOWANCES.get(&sender, &spender);
let new_allowance = internal::u256_add(¤t, &added_value);
internal::approve(sender, spender, new_allowance);
ret::u32(1)
}
}
nexus_fn! {
fn decreaseAllowance(spender: Address, subtracted_value: U256) {
let sender = Blockchain::msg.sender();
let current = ALLOWANCES.get(&sender, &spender);
if internal::u256_lt(¤t, &subtracted_value) {
panic!("Allowance underflow");
}
let new_allowance = internal::u256_sub(¤t, &subtracted_value);
internal::approve(sender, spender, new_allowance);
ret::u32(1)
}
}
nexus_fn! {
fn allowance(owner: Address, spender: Address) {
let amount = ALLOWANCES.get(&owner, &spender);
let out = internal::u256_to_bytes(amount);
ret::u256(&out)
}
}
nexus_fn! {
fn transferFrom(from: Address, to: Address, amount: U256) {
let _guard = ReentrancyGuard::new();
let spender = Blockchain::msg.sender();
let success = internal::transfer_from(spender, from, to, amount);
if success { ret::u32(1) } else { ret::u32(0) }
}
}