EOSCrowdsale Contract Inspect

项目地址

项目目录

├── 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
);

...
}

wmuldapp_math 提供的一个方法。 dapp_math 中定义两种精度:WADRAY ,分别代表 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 更方便创建大型高频的分布式应用。

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.