Bancor Inspect

in #bancor7 years ago

Bancor 协议主要的目的是为了解决现有代币价格发现和流通机制的问题。通过 Bancor 协议,代币价格是通过准备金兑换比例确定,而非传统的通过实时交易定价。通过准备金机制,可以让用户自定义的代币也具有流通性。

Bancor 搭建了一个社区平台,用户可以参与其中构建自己的社群,发行自己的代币,开设自己的网络商店。通过准备金兑换机制,让不同社区间的代币流通成为可能,促进整个社区生态更好的发展。

本文主要在技术层面分析 Bancor的众筹合约,以及整个代币互换的业务逻辑。

项目结构

├── LICENSE
├── README.md
├── package.json
└── solidity
    ├── contracts              // 合约源码
    ├── hypothesis             // 公式测试
    ├── jupyter                // 公式测试
    ├── migrations             // 部署脚本
    ├── python                 // 公式测试
    ├── test                   // 合约测试
    └── truffle-config.js

本项目是基于 TruffleFramework 框架开发。 Truffle 框架使用 NodeJS 实现,提供了方便易用的部署、测试功能。

/本项目目录结构不是一个标准的 Truffle 项目/

众筹合约

变量

uint256 public constant DURATION = 14 days;           // 众筹周期
uint256 public constant MAX_GAS_PRICE = 50000000000 wei;  // GAS 上限 5GWEI
uint256 public startTime = 0;                         // 开始时间
uint256 public endTime = 0;                           // 结束时间
uint256 public totalEtherCap = 1000000 ether;         // 众筹上限,临时数值
uint256 public totalEtherContributed = 0;             // 已经众筹数量
bytes32 public realEtherCapHash;                // 设置 CAP 是用来验证的 hash
address public beneficiary = 0x0;               // 众筹 ETH 接受地址

构造函数

function CrowdsaleController(ISmartToken _token, uint256 _startTime, address _beneficiary, address _btcs, bytes32 _realEtherCapHash)
    SmartTokenController(_token)
  validAddress(_beneficiary)
  validAddress(_btcs)
  earlierThan(_startTime)
  validAmount(uint256(_realEtherCapHash))
{
    startTime = _startTime;
    endTime = startTime + DURATION;
    beneficiary = _beneficiary;
    btcs = _btcs;
    realEtherCapHash = _realEtherCapHash;
}

这里是参数检查和变量赋值。

Bancor 这次 ICO 是通过时间来确定开始结束时机。以往都是通过块高度来决定。精度是跟网络出块速度相关,大概在秒级。

    modifier earlierThan(uint256 _time) {
        assert(now < _time);
        _;
    }

另外在初始化的时候,设置 TokenController : SmartTokenController(_token)

转账

  function contributeETH()
        public
        payable
        between(startTime, endTime)
        returns (uint256 amount)
    {
        return processContribution();
    }

between 验证交易时间合法性。

function processContribution() private
        active
        etherCapNotReached(msg.value) // 有没有到上限
        validGasPrice                 // GAS 是否合理
        returns (uint256 amount)
{
    uint256 tokenAmount = computeReturn(msg.value);  // 计算兑换比例
    assert(beneficiary.send(msg.value));             // 先把 ETH 转移到受益人账户
  totalEtherContributed = safeAdd(totalEtherContributed, msg.value);
  token.issue(msg.sender, tokenAmount);  // 发放 token
  token.issue(beneficiary, tokenAmount);

  return tokenAmount;
}

active 验证 token 的所有者是不是合约自己

modifier active() {
    assert(token.owner() == address(this));
  _;
}

validGasPrice 在 Bancor 和 Status 的 ICO 中都有用到。我们知道以太坊的交易是 GAS 越高的交易会优先被打包。这种机制在 ICO 场景中是不公平的。所以通过限制 GAS,第一提高公平性,第二为网络上其他紧急的交易预留通道。

软/硬顶

function computeRealCap(uint256 _cap, uint256 _key) public constant returns (bytes32)
{
    return keccak256(_cap, _key);
}

function enableRealCap(uint256 _cap, uint256 _key)
    public
    ownerOnly
    active
    between(startTime, endTime)
    validEtherCap(_cap, _key)
{
    require(_cap < totalEtherCap); // validate input
    totalEtherCap = _cap;
}

computeRealCap 的计算出来的 hash 会在部署合约时传给构造函数。这个时候,CAP 是不可知的。等到一定时机之后,合约拥有者通过调用 enableRealCap 设置 totalEtherCap 变量,从而公布硬顶。这样做可以防止资本方通过确定市场比例参与 ICO 进而控制代币价格。

Bancor 协议

准备金

struct Reserve {
    uint256 virtualBalance;         // 数量
    uint8 ratio;                    // CRR(准备金率)
    bool isVirtualBalanceEnabled; 
  bool isPurchaseEnabled;
    bool isSet;
}

Bancor 中用户发行的代币都需要有准备金,准备金可以是 ETH、Bancor 等 Token。每种 Token 可以有多种准备金,但是所有准备金的 CRR 之和不能超过 100%。

买入 Token

function buy(IERC20Token _reserveToken, uint256 _depositAmount, uint256 _minReturn)
    public
    validAmount(_minReturn)
    returns (uint256 amount)
{
    // 计算兑换比例
    amount = getPurchaseReturn(_reserveToken, _depositAmount);
    assert(amount != 0 && amount >= _minReturn); // 最小购买数

    // 增加准备金数量
    Reserve reserve = reserves[_reserveToken];
    if (reserve.isVirtualBalanceEnabled)
        reserve.virtualBalance = safeAdd(reserve.virtualBalance, _depositAmount);

    // 准备金从购买者账户转账到准备金代币合约地址
    assert(_reserveToken.transferFrom(msg.sender, this, _depositAmount));

    // 发放 Token
    token.issue(msg.sender, amount);
    ...
}
function getPurchaseReturn(IERC20Token _reserveToken, uint256 _depositAmount)
    public
    constant
    active
    validReserve(_reserveToken)
    returns (uint256 amount)
{
    Reserve reserve = reserves[_reserveToken];
    require(reserve.isPurchaseEnabled);

    // 通过公式计算兑换量
    uint256 tokenSupply = token.totalSupply();
    uint256 reserveBalance = getReserveBalance(_reserveToken);
    return formula.calculatePurchaseReturn(tokenSupply, reserveBalance, reserve.ratio, _depositAmount);
}
// Return = _supply * ((1 + _depositAmount / _reserveBalance) ^ (_reserveRatio / 100) - 1)

function calculatePurchaseReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _depositAmount) public constant returns (uint256)
{
    ...

    uint256 baseN = safeAdd(_depositAmount, _reserveBalance);
    uint256 temp;

    // special case if the CRR = 100
    if (_reserveRatio == 100) {
        temp = safeMul(_supply, baseN) / _reserveBalance;
        return safeSub(temp, _supply); 
    }

    uint256 resN = power(baseN, _reserveBalance, _reserveRatio, 100);

    temp = safeMul(_supply, resN) / FIXED_ONE;

    uint256 result =  safeSub(temp, _supply);
    // from the result, we deduct the minimal increment, which is a         
    // function of S and precision.       
    return safeSub(result, _supply / 0x100000000);
}

根据公式可以看出,随着准备金越来越多,相同准备金购买的 Token 越来越少,Token价值越来越高。

卖出 Token

function sell(IERC20Token _reserveToken, uint256 _sellAmount, uint256 _minReturn)
    public
    validAmount(_minReturn)
    returns (uint256 amount)
{
    ...

    // 验证提现数据
    amount = getSaleReturn(_reserveToken, _sellAmount);
    assert(amount != 0 && amount >= _minReturn);

    // 提现数量需小于等于准备金数量
    uint256 reserveBalance = getReserveBalance(_reserveToken);
    assert(amount <= reserveBalance);

    // 提现小于准备金余额或者提现等于准备金余额并且卖出代币数量等于供应量
    uint256 tokenSupply = token.totalSupply();
    assert(amount < reserveBalance || _sellAmount == tokenSupply);
    
    // 更新准备金数量
    Reserve reserve = reserves[_reserveToken];
    if (reserve.isVirtualBalanceEnabled)
        reserve.virtualBalance = safeSub(reserve.virtualBalance, amount);

    token.destroy(msg.sender, _sellAmount); // 销毁用户交易的代币数量
    assert(_reserveToken.transfer(msg.sender, amount)); // 准备金发送给用户

    ...
}
function getSaleReturn(IERC20Token _reserveToken, uint256 _sellAmount, uint256 _totalSupply)
    private
    constant
    active
    validReserve(_reserveToken)
    validAmount(_totalSupply)
    returns (uint256 amount)
{
    Reserve reserve = reserves[_reserveToken];
    uint256 reserveBalance = getReserveBalance(_reserveToken);
    return formula.calculateSaleReturn(_totalSupply, reserveBalance, reserve.ratio, _sellAmount);
}
// Return = _reserveBalance * (1 - (1 - _sellAmount / _supply) ^ (1 / (_reserveRatio / 100)))
    
function calculateSaleReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _sellAmount) public constant returns (uint256)
{
    ...

    uint256 baseN = safeSub(_supply, _sellAmount);
    uint256 temp1;
    uint256 temp2;

    // special case if the CRR = 100
    if (_reserveRatio == 100) {
        temp1 = safeMul(_reserveBalance, _supply);
        temp2 = safeMul(_reserveBalance, baseN);
        return safeSub(temp1, temp2) / _supply;
    }

    // special case for selling the entire supply
    if (_sellAmount == _supply)
        return _reserveBalance;

    uint256 resN = power(_supply, baseN, 100, _reserveRatio);

    temp1 = safeMul(_reserveBalance, resN);
    temp2 = safeMul(_reserveBalance, FIXED_ONE);

    uint256 result = safeSub(temp1, temp2) / resN;

    // from the result, we deduct the minimal increment, which is a         
    // function of R and precision.       
    return safeSub(result, _reserveBalance / 0x100000000);
}

通过以上卖出、买入的操作,就能实现 Token 的流通。

总结

Bancor 的出现对众筹、粉丝经济等方面有极大的促进作用。各个社群的货币互通,可以提高社群的活力,促进社群的发展。

Bancor 提供的价格发现机制,让社群越活跃,其代币越值钱。社群组织者更好盈利,继续扩大社群规模,提高服务质量。社群参与者由于 Token 价值的提高更愿意参加社群活动。

Sort:  

Thank you, Great post! @tooooolong

你的文已看完,我已申请关注了,我也写了第一篇文,有空请阅下~
个人认为NEVERDIE ICO 也不错,在我个人热帖里,Neverdie.com
https://steemit.com/cn/@maxtill94/neverdie-ico-erc20-ico

Congratulations @tooooolong! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Do not miss the last post from @steemitboard:

The Steem community has lost an epic member! Farewell @woflhart!
SteemitBoard - Witness Update
Do not miss the coming Rocky Mountain Steem Meetup and get a new community badge!
Vote for @Steemitboard as a witness to get one more award and increased upvotes!

Coin Marketplace

STEEM 0.25
TRX 0.20
JST 0.037
BTC 93400.01
ETH 3410.75
USDT 1.00
SBD 3.80