EOSCrowdsale Contract Inspect
这两天 EOS 众筹正火,蹭个热点,技术面分析下 EOS的众筹合约
项目地址
https://github.com/EOSIO/eos-token-distribution
项目目录
├── Dappfile // dapphub 出品的智能合约开发工具
├── Makefile
├── bin
│ └── deploy // 部署脚本
├── lib // 第三方依赖
│ ├── ds-auth // 权限控制
│ ├── ds-exec
│ ├── ds-guard
│ ├── ds-math // 数学运算
│ ├── ds-test // 测试框架
│ ├── ds-token // Token 框架
│ └── gnosis-multisig
└── src // 源码
├── eos.sol
└── eos.t.sol
本项目使用 Dapphub 出品的智能合约开发框架 dapp 开发。Dapphub 开源了很多实用的库,大大简化了智能合约的开发成本。
代码分析
变量
DSToken public EOS; // EOS Token
uint128 public totalSupply; // Token 总量
uint128 public foundersAllocation; // 开发团队保留份额
string public foundersKey; // 保留份额 Holder Address
uint public openTime; // window 0 开始时间
uint public createFirstDay; // window 0 供应量
uint public startTime; // window 1 开始时间
uint public numberOfDays; // window 总数
uint public createPerDay; // 每日供应量
EOS 的 ICO 规则为:前五天为一个 window 总共发行 2 亿 Token。五天之后,每23个小时为一个window,发行 2 百万 Token。总共发行 10 亿 Token。
构造函数
// src/eos.sol
function EOSSale(
uint _numberOfDays,
uint128 _totalSupply,
uint _openTime,
uint _startTime,
uint128 _foundersAllocation,
string _foundersKey
) {
...
// window 0 Token 供应量
createFirstDay = wmul(totalSupply, 0.2 ether);
// window 1 以及以后的每天 Token 供应量
createPerDay = div(
sub(sub(totalSupply, foundersAllocation), createFirstDay),
numberOfDays
);
...
}
wmul
是 dapp_math
提供的一个方法。 dapp_math 中定义两种精度:WAD
和 RAY
,分别代表 18 位和 27 位的精度。以 w 开头表示该运算精度为 18 位。
由于 solidity 里面没有 float 类型,想要乘以 0.2 可以通过 乘以 0.2 ether
实现。
初始化 Token
// src/eos.sol
function initialize(DSToken eos) auth {
...
EOS = eos;
// 设置供应总量
EOS.mint(totalSupply);
// 保留 Token 发往 0xb1 这个地址
EOS.push(0xb1, foundersAllocation);
keys[0xb1] = foundersKey;
}
mint
方法是 dapp_token 提供,用来设置 Token 供应总量。
开发团队保留的份额被发送到 0xb1
。这个地址是无人可用的,等于丢掉此数量的 Token ,但是又保证 Token 总供应量不变。EOS 团队不需要这个代币,因为在 EOS 上线之后,现在的 Token 会兑换为 EOS 链上的 Token。
window time
// Each window is 23 hours long so that end-of-window rotates
// around the clock for all timezones.
function dayFor(uint timestamp) constant returns (uint) {
return timestamp < startTime
? 0
: sub(timestamp, startTime) / 23 hours + 1;
}
前五天都是 window 0 。之后每23小时为一个 window 。这么做的好处是让每个 window 的开始时间是滚动的,不同时区的投资者更方便参与。
购买逻辑
function buyWithLimit(uint day, uint limit) payable {
// 限制时间
assert(time() >= openTime && today() <= numberOfDays);
// 最小购买额度 0.01 ether
assert(msg.value >= 0.01 ether);
// 购买记录
userBuys[day][msg.sender] += msg.value;
dailyTotals[day] += msg.value;
}
Token 兑换
function claim(uint day) {
// 防止重复兑换
if (claimed[day][msg.sender] || dailyTotals[day] == 0) {
return;
}
// This will have small rounding errors, but the token is
// going to be truncated to 8 decimal places or less anyway
// when launched on its own chain.
var dailyTotal = cast(dailyTotals[day]);
var userTotal = cast(userBuys[day][msg.sender]);
// 指定 window 的 Token 供应量除以此 window 的 eth 总量
// 得到兑换比例
var price = wdiv(cast(createOnDay(day)), dailyTotal);
// 兑换比例乘以指定 window 中此用户支付的 eth 数量得到兑换总量
var reward = wmul(price, userTotal);
// 记录兑换标志
claimed[day][msg.sender] = true;
// 执行转账
EOS.push(msg.sender, reward);
}
注册 EOS 共钥
function register(string key) {
// 众筹结束之后不再提供注册
assert(today() <= numberOfDays + 1);
assert(bytes(key).length <= 64);
keys[msg.sender] = key;
}
EOS 团队要求投资者在众筹结束之前自行生成 EOS 公私钥,并将生成的共钥注册在合约中。这样在 EOS 正式上线之后,用户可以兑换 EOS 上的 Token 。
ETH 转移
function collect() auth {
// window 0 不能转移
assert(today() > 0);
// 将 eth 转移给调用者
exec(msg.sender, this.balance);
}
auth
是 dapp_auth 提供的权限控制方法,保证 collect
函数只能被合约 owner 执行。
安全性分析
EOS 众筹合约使用了 dapp_auth 提供的权限控制功能。
// src/eos.sol
// 继承 DSAuth
contract EOSSale is DSAuth {
}
// lib/ds-auth/src/auth.sol
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
function DSAuth() {
owner = msg.sender;
}
}
DSAuth
提供的默认权限控制是基于 owner 的。默认初始化行为将合约创建者设置为 owner 。同时提供 setOwner
函数,可以转移控制权。
function setOwner(address owner_)
auth
{
owner = owner_;
LogSetOwner(owner);
}
setOwner
也需要使用 auth
验证权限。
modifier auth {
assert(isAuthorized(msg.sender, msg.sig));
_;
}
function isAuthorized(address src, bytes4 sig)
internal returns (bool)
{
// 如果调用者是合约自己,通过。
if (src == address(this)) {
return true;
// 如果调用者是 owner ,通过。
} else if (src == owner) {
return true;
// 如果 authority 未设置,失败。
} else if (authority == DSAuthority(0)) {
return false;
} else {
// 检查调用者是否有权限。
return authority.canCall(src, this, sig);
}
}
在默认行为下 authority 未设置,所以只有 owner 验证。
可以看到,这个合约还可以通过设置自定义的 DSAuthority 来扩展权限验证的逻辑。
contract DSAuthority {
function canCall(
address src, address dst, bytes4 sig
) constant returns (bool);
}
contract DSAuth is DSAuthEvents {
function setAuthority(DSAuthority authority_)
auth
{
authority = authority_;
LogSetAuthority(authority);
}
}
我们只需要在自己的合约中继承 DSAuthority ,实现 canCall 方法。
然后将实例传入 setAuthority 就可以设置特定的权限逻辑。
商业价值分析
EOS 的主要特性:
- 免费试用
- 合约可以升级
- 出块速度快,达到平均 1.5s
- 串行/并行性能强,可以达到百万级交易处理规模
- 账户系统,权限系统等
- 可插拔的合约虚拟机
EOS 更方便创建大型高频的分布式应用。
Nice
Very cool post, I can learn a lot of new things from you :D
happy to follow you
What is this? xd :D
我从不买iCO
Postnya very useful and hope you are lucky for future in this steemit :)
But do not forget the upvote on my blog too :D
#Thank's
Toooo long to read everything :D
Thanks for sharing, nice
強大的技術貼
有深度的技术贴,棒棒哒