Overview
NEP-20 is the fungible token standard for NEXUS, fully compatible with ERC-20 for cross-contract calls.Interface
Required Functions
| Function | Arguments | Returns | Description |
|---|---|---|---|
name | - | string | Token name |
symbol | - | string | Token symbol |
decimals | - | u32 | Decimal places |
totalSupply | - | U256 | Total supply |
balanceOf | address | U256 | Balance of address |
transfer | to, amount | bool | Transfer tokens |
approve | spender, amount | bool | Approve spending |
allowance | owner, spender | U256 | Get allowance |
transferFrom | from, to, amount | bool | Transfer from |
Events
| Event | Description |
|---|---|
Transfer | Emitted on token transfer |
Approval | Emitted when allowance is set |
Production Implementation
Copy
//! NEP-20 Token Standard - Production Implementation
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 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::*;
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 supply > U256::zero() {
mint(sender, supply);
}
}
pub fn mint(to: Address, amount: U256) {
let current_supply = TOTAL_SUPPLY.get(&b"total".as_slice());
TOTAL_SUPPLY.set(&b"total".as_slice(), current_supply.add(amount));
let current_bal = BALANCES.get(&to);
BALANCES.set(&to, current_bal.add(amount));
emit("Transfer", &[]);
}
pub fn transfer(sender: Address, to: Address, amount: U256) -> bool {
let sender_bal = BALANCES.get(&sender);
if sender_bal < amount {
return false;
}
BALANCES.set(&sender, sender_bal.sub(amount));
let to_bal = BALANCES.get(&to);
BALANCES.set(&to, to_bal.add(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 {
let allowed = ALLOWANCES.get(&from, &spender);
if allowed < amount {
return false;
}
// Update Allowance (unless max approval)
if allowed != U256::max_value() {
ALLOWANCES.set(&from, &spender, allowed.sub(amount));
}
internal::transfer(from, to, amount)
}
}
// ABI Entry Points
nexus_fn! {
fn init_token(name: String, symbol: String, decimals: u64, supply: U256) {
internal::init(name, symbol, decimals as u8, supply);
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 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 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) }
}
}
Key Features
Solidity-Compatible Types
Copy
use nexus_sdk::solidity::{*, SafeMath};
use nexus_sdk::solidity::uint256 as U256;
// Mappings work like Solidity
static BALANCES: Mapping<Address, U256> = Mapping::new(b"bal");
static ALLOWANCES: DoubleMapping<Address, Address, U256> = DoubleMapping::new(b"allow");
SafeMath Operations
Copy
// Safe arithmetic that reverts on overflow
let new_balance = current_balance.add(amount);
let remaining = balance.sub(amount);
let product = a.mul(b);
let quotient = a.div(b);
Reentrancy Protection
Copy
nexus_fn! {
fn transfer(to: Address, amount: U256) {
let _guard = ReentrancyGuard::new(); // Auto-releases on drop
// ... transfer logic
}
}
nexus_fn! Macro
Thenexus_fn! macro handles:
- ABI encoding/decoding
- Function export
- Error handling
Copy
nexus_fn! {
fn balanceOf(addr: Address) {
let bal = BALANCES.get(&addr);
let mut out = [0u8; 32];
bal.to_little_endian(&mut out);
ret::u256(&out)
}
}
Deployment
Copy
nexus contract deploy \
--wasm ./target/wasm32-unknown-unknown/release/nep20.wasm \
--name my_token \
--from my-wallet \
--init-args '["MyToken","MTK","18","1000000000000000000"]'
Usage
Copy
const token = new TokenContract(client, contractId);
// Query
const balance = await token.balanceOf(address);
const name = await token.name();
// Transfer
await token.transfer(recipient, '1000000');
// Approve and transferFrom
await token.approve(spender, '1000000');
await token.transferFrom(owner, recipient, '500000');