Skip to content

Instantly share code, notes, and snippets.

@dicarlo2
Created May 8, 2018 09:07
Show Gist options
  • Save dicarlo2/e5b8bc499b3b94f4618dee87fd738585 to your computer and use it in GitHub Desktop.
Save dicarlo2/e5b8bc499b3b94f4618dee87fd738585 to your computer and use it in GitHub Desktop.
import {
Address,
Event,
Fixed,
SmartContract,
notify,
verifySender,
} from '@neo-one/smart-contract';
class Transfer<Decimals extends number> extends Event {
constructor(from: Address, to: Address, amount: Fixed<Decimals>) {
super(from, to, amount);
}
}
class Approve<Decimals extends number> extends Event {
constructor(owner: Address, spender: Address, amount: Fixed<Decimals>) {
super(owner, spender, amount);
}
}
export abstract class Token<Decimals extends number> extends SmartContract {
public abstract readonly name: string;
public abstract readonly decimals: Decimals;
public abstract readonly symbol: string;
protected readonly owner: Address;
private supply: Fixed<Decimals> = 0;
private readonly balances: Map<Address, Fixed<Decimals>> = new Map();
private readonly allowances: Map<Address, Map<Address, Fixed<Decimals>>> = new Map();
constructor(owner: Address) {
super();
this.owner = owner;
}
public transfer(
from: Address,
to: Address,
amount: Fixed<Decimals>,
): void {
verifySender(from);
this.doTransfer(from, to, amount);
}
public transferFrom(
from: Address,
to: Address,
amount: Fixed<Decimals>,
): void {
const available = this.allowance(from, to);
if (available < amount) {
throw new Error('Insufficient funds approved');
}
this.doTransfer(from, to, amount);
this.setAllowance(from, to, available - amount);
}
public approve(
owner: Address,
spender: Address,
amount: Fixed<Decimals>,
): void {
verifySender(owner);
const fromValue = this.balanceOf(owner);
if (fromValue < amount) {
throw new Error('Insufficient funds');
}
this.setAllowance(owner, spender, this.allowance(owner, spender) + amount);
notify(new Approve(owner, spender, amount));
}
public balanceOf(addr: Address): Fixed<Decimals> {
return this.balances.get(addr) || 0;
}
public allowance(owner: Address, spender: Address): Fixed<Decimals> {
return (this.allowances.get(owner) || new Map()).get(spender) || 0;
}
public get totalSupply(): Fixed<Decimals> {
return this.supply;
}
protected issue(addr: Address, amount: Fixed<Decimals>): void {
this.balances.set(addr, this.balanceOf(addr) + amount);
this.supply += amount;
notify(new Transfer(this.address, addr, amount));
}
private doTransfer(
from: Address,
to: Address,
amount: Fixed<Decimals>,
): void {
if (amount <= 0) {
throw new Error('Invalid amount');
}
const fromValue = this.balanceOf(from);
if (fromValue < amount) {
throw new Error('Insufficient funds');
}
this.balances.set(from, fromValue - amount);
this.balances.set(to, this.balanceOf(to) + amount);
notify(new Transfer(from, to, amount));
}
private setAllowance(
from: Address,
to: Address,
amount: Fixed<Decimals>,
): void {
let allowances = this.allowances.get(from);
if (allowances == null) {
allowances = new Map();
}
allowances.set(to, amount);
this.allowances.set(from, allowances);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment