Estimated read time: 10 minutes
Build a Token Contract
VRC-20: The Valdium token standard
VRC-20 is Valdium's fungible token standard — conceptually equivalent to ERC-20, but written in plain JavaScript. No pragma, no imports, no compiler. Just functions in a sandbox.
Full VRC-20 implementation
// vrc20.js — Valdium fungible token
const BALANCE_PREFIX = 'b:';
const ALLOWANCE_PREFIX = 'a:';
({
init(args) {
assert(args.totalSupply, 'totalSupply required');
storage.set('name', args.name || 'Valdium Token');
storage.set('symbol', args.symbol || 'VRC');
storage.set('decimals', '18');
storage.set('supply', String(args.totalSupply));
storage.set(BALANCE_PREFIX + msg.sender, String(args.totalSupply));
emit('Transfer', { from: null, to: msg.sender, value: String(args.totalSupply) });
},
name() { return storage.get('name'); },
symbol() { return storage.get('symbol'); },
decimals() { return Number(storage.get('decimals')); },
totalSupply() { return storage.get('supply'); },
balanceOf(address) {
return storage.get(BALANCE_PREFIX + address) || '0';
},
transfer(to, amount) {
const from = msg.sender;
const amt = BigInt(amount);
assert(amt > 0n, 'amount must be positive');
const fromBal = BigInt(storage.get(BALANCE_PREFIX + from) || '0');
assert(fromBal >= amt, 'insufficient balance');
const toBal = BigInt(storage.get(BALANCE_PREFIX + to) || '0');
storage.set(BALANCE_PREFIX + from, String(fromBal - amt));
storage.set(BALANCE_PREFIX + to, String(toBal + amt));
emit('Transfer', { from, to, value: String(amt) });
return true;
},
approve(spender, amount) {
storage.set(ALLOWANCE_PREFIX + msg.sender + ':' + spender, String(amount));
emit('Approval', { owner: msg.sender, spender, value: String(amount) });
return true;
},
allowance(owner, spender) {
return storage.get(ALLOWANCE_PREFIX + owner + ':' + spender) || '0';
},
transferFrom(from, to, amount) {
const spender = msg.sender;
const amt = BigInt(amount);
const allowed = BigInt(storage.get(ALLOWANCE_PREFIX + from + ':' + spender) || '0');
assert(allowed >= amt, 'allowance exceeded');
const fromBal = BigInt(storage.get(BALANCE_PREFIX + from) || '0');
assert(fromBal >= amt, 'insufficient balance');
const toBal = BigInt(storage.get(BALANCE_PREFIX + to) || '0');
storage.set(ALLOWANCE_PREFIX + from + ':' + spender, String(allowed - amt));
storage.set(BALANCE_PREFIX + from, String(fromBal - amt));
storage.set(BALANCE_PREFIX + to, String(toBal + amt));
emit('Transfer', { from, to, value: String(amt) });
return true;
},
});Deploy
valdium deploy vrc20.js --from alice --args '{"name":"My Token","symbol":"MTK","totalSupply":"1000000000000000000000000"}'The totalSupply is in wei (1e18 = 1 token). 1,000,000 MTK = 1000000000000000000000000.
Interact
valdium call 0xContract... balanceOf 0xAlice...
valdium call 0xContract... transfer 0xBob... 1000000000000000000 --from alice