Skip to main content

ERC20 Token

A complete example of an ERC20 implementation using the AssemblyScript Stylus SDK.

Complete Contract: You can view the full ERC20 contract implementation here.

Contract Structure

Events

Events record token activity for external indexing:

const Transfer = EventFactory.create<[Address, Address, U256]>({
indexed: [true, true, false],
});

const Approval = EventFactory.create<[Address, Address, U256]>({
indexed: [true, true, false],
});

Storage State

State variables that persist on the blockchain:

@Contract
export class ERC20Full {
balances: Mapping<Address, U256> = new Mapping<Address, U256>();
allowances: MappingNested<Address, Address, U256> = new MappingNested<Address, Address, U256>();
totalSupplyValue: U256;
nameValue: Str;
symbolValue: Str;

Constructor

Initializes the token with name and symbol:

  constructor(_name: string, _symbol: string) {
const nameStr = StrFactory.fromString(_name);
const symbolStr = StrFactory.fromString(_symbol);
this.nameValue = nameStr;
this.symbolValue = symbolStr;
this.totalSupplyValue = U256Factory.fromString("0");
}

View Functions

Read-only methods for querying information:

  @View
name(): Str {
return this.nameValue;
}

@View
symbol(): Str {
return this.symbolValue;
}

@View
totalSupply(): U256 {
return this.totalSupplyValue;
}

@View
balanceOf(account: Address): U256 {
return this.balances.get(account);
}

@View
allowance(owner: Address, spender: Address): U256 {
return this.allowances.get(owner, spender);
}

Transfer Function

Transfers tokens between accounts:

  @External
transfer(to: Address, amount: U256): boolean {
const sender = msg.sender;
const senderBal = this.balances.get(sender);
if (senderBal < amount) {
return false;
}

this.balances.set(sender, senderBal.sub(amount));
const recvBal = this.balances.get(to);
this.balances.set(to, recvBal.add(amount));

Transfer.emit(sender, to, amount);
return true;
}

Approval System

Allows third parties to spend tokens on your behalf:

  @External
approve(spender: Address, amount: U256): boolean {
const owner = msg.sender;
allowances.set(owner, spender, amount);
Approval.emit(owner, spender, amount);
return true;
}

@External
transferFrom(from: Address, to: Address, amount: U256): boolean {
const spender = msg.sender;
const allowed = this.allowances.get(from, spender);
if (allowed < amount) {
return false;
}

const fromBal = this.balances.get(from);
if (fromBal < amount) {
return false;
}

this.balances.set(from, fromBal.sub(amount));
const toBal = this.balances.get(to);
this.balances.set(to, toBal.add(amount));
this.allowances.set(from, spender, allowed.sub(amount));

Transfer.emit(from, to, amount);
return true;
}

Mint & Burn

Functions to mint and burn tokens:

  @External
mint(to: Address, amount: U256): void {
this.totalSupplyValue = this.totalSupplyValue.add(amount);
const toAmount = this.balances.get(to);
this.balances.set(to, toAmount.add(amount));

const AddressZero = AddressFactory.fromString("0x0000000000000000000000000000000000000000");
Transfer.emit(AddressZero, to, amount);
}

@External
burn(amount: U256): void {
const sender = msg.sender;
const senderBal = this.balances.get(sender);
if (senderBal < amount) {
return;
}

this.balances.set(sender, senderBal.sub(amount));
this.totalSupplyValue = this.totalSupplyValue.sub(amount);

const AddressZero = AddressFactory.fromString("0x0000000000000000000000000000000000000000");
Transfer.emit(sender, AddressZero, amount);
}
}