Skip to content

Instantly share code, notes, and snippets.

@kidwai
Created September 14, 2017 17:02
Show Gist options
  • Save kidwai/0f980cfcf4ab8f8febc6419532ef12b1 to your computer and use it in GitHub Desktop.
Save kidwai/0f980cfcf4ab8f8febc6419532ef12b1 to your computer and use it in GitHub Desktop.
A rough sketch of a token library. There are probably problems.
/**
* A simple example of a token sale that makes use of a library with
* a simple transfer function and a token struct. This is a biased implementation
* that mimics the types of operations found in most current token implementations.
* The purpose is only to illustrate the idea. This is nothing novel and turns out
* to be very similar to work by Open Zeppelin (https://blog.aragon.one/library-driven-development-in-solidity-2bebcaf88736).
*
* This can be generalized to support multiple sales or auctions. Questions surrounding issuance
* structure and governance are imperative, no different from ERC20 tokens.
*
* Elements of approval are omitted for the moment for simplicity.
**/
pragma solidity ^0.4.11;
library TokenLib {
// Some basic properties found in token contracts.
// The debatable "admin" property is made only to reflect current
// implementation. This is not necessarily the most trustless design.
struct Token {
address admin;
uint256 total_supply;
mapping (address => uint256) balances;
Sale sale;
}
struct Sale {
uint256 start_block;
uint256 end_block;
uint256 price;
}
/**
* @dev Initializes a token struct's properties.
* @param self A token struct - passed as first argument by default when called through a token struct [e.g. tokenInstance.init(..) ]
* @param admin The 'admin', the address responsible for issuing administrative transactions. This is explicitly provided to allow for the use of a multisignature contract.
* @param total_supply The total supply of tokens.
* @param founders The addresses of the founders.
* @param allocations The allocations to the respective founders.
* @param sale_start The block at which to commence the token sale.
* @param sale_end The block at which to end the sale.
* @param sale_price The price at which to sell the token.
**/
function init (
Token storage self,
address admin,
uint256 total_supply,
address[] founders,
uint256[] allocations,
uint256 sale_start,
uint256 sale_end,
uint256 sale_price
) onlyUninitialized (self) {
self.admin = admin; // set the token's admin address
// a sample of basic properties of a token
self.total_supply = total_supply;
self.sale.start_block = sale_start;
self.sale.end_block = sale_end;
self.sale.price = sale_price;
// give founders their hard-earned money
for (uint8 i = 0 ; i < founders.length; i++) {
self.balances[founders[i]] = allocations[i];
}
}
/**
* @dev Buy a quantity of tokens when on sale based on the transaction value.
* @param self The token struct on which to operate.
**/
function buy (
Token storage self
) onSale(self) {
self.balances[msg.sender] += msg.value/self.sale.price;
}
// Transfer '_value' tokens to the address '_to'.
function transfer (
Token storage self,
address _to,
uint256 _value
) onlyInitialized (self)
{
require(self.balances[msg.sender] >= _value);
self.balances[msg.sender] -= _value;
self.balances[_to] += _value;
Transfer(msg.sender, _to, _value);
}
function balanceOf (Token storage self, address _addr) constant returns (uint256) {
return self.balances[_addr];
}
// checks if a token is on sale
modifier onSale (Token token) {
require (token.sale.start_block <= block.number && block.number < token.sale.end_block);
_;
}
modifier onlyUninitialized (Token token) {
require (token.admin == 0);
_;
}
modifier onlyInitialized (Token token) {
require (token.admin != 0);
_;
}
event Transfer (address indexed from, address to, uint256 value);
}
// A simple example of a sale of 100 coins at 1000 Wei / coin
contract FooCoin {
using TokenLib for TokenLib.Token;
TokenLib.Token token;
function FooCoin (
address[] founders,
uint256[] allocations
) {
token.init(msg.sender,
100,
founders,
allocations,
block.number + 10,
block.number + 20,
1000);
}
function balanceOf (address _addr) constant returns (uint256) {
// use the library implementation
return token.balanceOf(_addr);
}
function transfer (address _to, uint256 _value)
{
return token.transfer(_to, _value);
}
function () payable {
// use the library implementation
token.buy();
}
}
@maurelian
Copy link

I like it a lot and agree that it's non-ideal that so much "standard" logic is being duplicated on chain.

A few questions/observations:

  • do you know how this will affect gas costs? There would be an extra message call, right?
  • I think standalone token and sale libs would make a bit more sense, not so much from a technical perspective, as it would for "cognitive barriers to adoption".
  • I put this into remix, a few functions are missing from fooCoin, and some other ERC20 spec functions like approve() are missing, but given this is just meant a proof of concept it's great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment