The original Bancor pricing formula, computing purchase and sale returns for Smart Tokens from supply, reserve and reserve ratio.
Historical Significance
The first deployed version of the bonding-curve style formula behind Bancor's reserve-based pricing, deployed 2017-06-22 for the BNT converter.
Key Facts
Description
Implements the Bancor price calculation. calculatePurchaseReturn and calculateSaleReturn take a Smart Token's supply, reserve balance and reserve ratio and return how many tokens a deposit buys or how much reserve a sale returns, using fixed-point logarithm and exponent functions.
Source Verified
Historian Categories
Spurious Dragon Era
Continued DoS protection. State trie clearing.
Bytecode Overview
Verified Source Available
Source verified on Etherscan.
Show source code (Solidity)
pragma solidity ^0.4.11;
/*
Overflow protected math functions
*/
contract SafeMath {
/**
constructor
*/
function SafeMath() {
}
/**
@dev returns the sum of _x and _y, asserts if the calculation overflows
@param _x value 1
@param _y value 2
@return sum
*/
function safeAdd(uint256 _x, uint256 _y) internal returns (uint256) {
uint256 z = _x + _y;
assert(z >= _x);
return z;
}
/**
@dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number
@param _x minuend
@param _y subtrahend
@return difference
*/
function safeSub(uint256 _x, uint256 _y) internal returns (uint256) {
assert(_x >= _y);
return _x - _y;
}
/**
@dev returns the product of multiplying _x by _y, asserts if the calculation overflows
@param _x factor 1
@param _y factor 2
@return product
*/
function safeMul(uint256 _x, uint256 _y) internal returns (uint256) {
uint256 z = _x * _y;
assert(_x == 0 || z / _x == _y);
return z;
}
}
/*
Bancor Formula interface
*/
contract IBancorFormula {
function calculatePurchaseReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _depositAmount) public constant returns (uint256);
function calculateSaleReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _sellAmount) public constant returns (uint256);
}
/*
Open issues:
- The formula is not yet super accurate, especially for very small/very high ratios
- Possibly support dynamic precision in the future
*/
contract BancorFormula is IBancorFormula, SafeMath {
uint8 constant PRECISION = 32; // fractional bits
uint256 constant FIXED_ONE = uint256(1) << PRECISION; // 0x100000000
uint256 constant FIXED_TWO = uint256(2) << PRECISION; // 0x200000000
uint256 constant MAX_VAL = uint256(1) << (256 - PRECISION); // 0x0000000100000000000000000000000000000000000000000000000000000000
string public version = '0.1';
function BancorFormula() {
}
/**
@dev given a token supply, reserve, CRR and a deposit amount (in the reserve token), calculates the return for a given change (in the main token)
Formula:
Return = _supply * ((1 + _depositAmount / _reserveBalance) ^ (_reserveRatio / 100) - 1)
@param _supply token total supply
@param _reserveBalance total reserve
@param _reserveRatio constant reserve ratio, 1-100
@param _depositAmount deposit amount, in reserve token
@return purchase return amount
*/
function calculatePurchaseReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _depositAmount) public constant returns (uint256) {
// validate input
require(_supply != 0 && _reserveBalance != 0 && _reserveRatio > 0 && _reserveRatio <= 100);
// special case for 0 deposit amount
if (_depositAmount == 0)
return 0;
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);
}
/**
@dev given a token supply, reserve, CRR and a sell amount (in the main token), calculates the return for a given change (in the reserve token)
Formula:
Return = _reserveBalance * (1 - (1 - _sellAmount / _supply) ^ (1 / (_reserveRatio / 100)))
@param _supply token total supply
@param _reserveBalance total reserve
@param _reserveRatio constant reserve ratio, 1-100
@param _sellAmount sell amount, in the token itself
@return sale return amount
*/
function calculateSaleReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _sellAmount) public constant returns (uint256) {
// validate input
require(_supply != 0 && _reserveBalance != 0 && _reserveRatio > 0 && _reserveRatio <= 100 && _sellAmount <= _supply);
// special case for 0 sell amount
if (_sellAmount == 0)
return 0;
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);
}
/**
@dev Calculate (_baseN / _baseD) ^ (_expN / _expD)
Returns result upshifted by PRECISION
This method is overflow-safe
*/
function power(uint256 _baseN, uint256 _baseD, uint32 _expN, uint32 _expD) constant returns (uint256 resN) {
uint256 logbase = ln(_baseN, _baseD);
// Not using safeDiv here, since safeDiv protects against
// precision loss. It's unavoidable, however
// Both `ln` and `fixedExp` are overflow-safe.
resN = fixedExp(safeMul(logbase, _expN) / _expD);
return resN;
}
/**
input range:
- numerator: [1, uint256_max >> PRECISION]
- denominator: [1, uint256_max >> PRECISION]
output range:
[0, 0x9b43d4f8d6]
This method asserts outside of bounds
*/
function ln(uint256 _numerator, uint256 _denominator) public constant returns (uint256) {
// denominator > numerator: less than one yields negative values. Unsupported
assert(_denominator <= _numerator);
// log(1) is the lowest we can go
assert(_denominator != 0 && _numerator != 0);
// Upper 32 bits are scaled off by PRECISION
assert(_numerator < MAX_VAL);
assert(_denominator < MAX_VAL);
return fixedLoge( (_numerator * FIXED_ONE) / _denominator);
}
/**
input range:
[0x100000000,uint256_max]
output range:
[0, 0x9b43d4f8d6]
This method asserts outside of bounds
*/
function fixedLoge(uint256 _x) constant returns (uint256 logE) {
/*
Since `fixedLog2_min` output range is max `0xdfffffffff`
(40 bits, or 5 bytes), we can use a very large approximation
for `ln(2)`. This one is used since it's the max accuracy
of Python `ln(2)`
0xb17217f7d1cf78 = ln(2) * (1 << 56)
*/
//Cannot represent negative numbers (below 1)
assert(_x >= FIXED_ONE);
uint256 log2 = fixedLog2(_x);
logE = (log2 * 0xb17217f7d1cf78) >> 56;
}
/**
Returns log2(x >> 32) << 32 [1]
So x is assumed to be already upshifted 32 bits, and
the result is also upshifted 32 bits.
[1] The function returns a number which is lower than the
actual value
input-range :
[0x100000000,uint256_max]
output-range:
[0,0xdfffffffff]
This method asserts outside of bounds
*/
function fixedLog2(uint256 _x) constant returns (uint256) {
// Numbers below 1 are negative.
assert( _x >= FIXED_ONE);
uint256 hi = 0;
while (_x >= FIXED_TWO) {
_x >>= 1;
hi += FIXED_ONE;
}
for (uint8 i = 0; i < PRECISION; ++i) {
_x = (_x * _x) / FIXED_ONE;
if (_x >= FIXED_TWO) {
_x >>= 1;
hi += uint256(1) << (PRECISION - 1 - i);
}
}
return hi;
}
/**
fixedExp is a 'protected' version of `fixedExpUnsafe`, which
asserts instead of overflows
*/
function fixedExp(uint256 _x) constant returns (uint256) {
assert(_x <= 0x386bfdba29);
return fixedExpUnsafe(_x);
}
/**
fixedExp
Calculates e^x according to maclauren summation:
e^x = 1+x+x^2/2!...+x^n/n!
and returns e^(x>>32) << 32, that is, upshifted for accuracy
Input range:
- Function ok at <= 242329958953
- Function fails at >= 242329958954
This method is is visible for testcases, but not meant for direct use.
The values in this method been generated via the following python snippet:
def calculateFactorials():
"""Method to print out the factorials for fixedExp"""
ni = []
ni.append( 295232799039604140847618609643520000000) # 34!
ITERATIONS = 34
for n in range( 1, ITERATIONS,1 ) :
ni.append(math.floor(ni[n - 1] / n))
print( "\n ".join(["xi = (xi * _x) >> PRECISION;\n res += xi * %s;" % hex(int(x)) for x in ni]))
*/
function fixedExpUnsafe(uint256 _x) constant returns (uint256) {
uint256 xi = FIXED_ONE;
uint256 res = 0xde1bc4d19efcac82445da75b00000000 * xi;
xi = (xi * _x) >> PRECISION;
res += xi * 0xde1bc4d19efcb0000000000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x6f0de268cf7e58000000000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x2504a0cd9a7f72000000000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x9412833669fdc800000000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x1d9d4d714865f500000000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x4ef8ce836bba8c0000000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0xb481d807d1aa68000000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x16903b00fa354d000000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x281cdaac677b3400000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x402e2aad725eb80000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x5d5a6c9f31fe24000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x7c7890d442a83000000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x9931ed540345280000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0xaf147cf24ce150000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0xbac08546b867d000000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0xbac08546b867d00000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0xafc441338061b8000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x9c3cabbc0056e000000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x839168328705c80000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x694120286c04a0000;
xi = (xi * _x) >> PRECISION;
res += xi * 0x50319e98b3d2c400;
xi = (xi * _x) >> PRECISION;
res += xi * 0x3a52a1e36b82020;
xi = (xi * _x) >> PRECISION;
res += xi * 0x289286e0fce002;
xi = (xi * _x) >> PRECISION;
res += xi * 0x1b0c59eb53400;
xi = (xi * _x) >> PRECISION;
res += xi * 0x114f95b55400;
xi = (xi * _x) >> PRECISION;
res += xi * 0xaa7210d200;
xi = (xi * _x) >> PRECISION;
res += xi * 0x650139600;
xi = (xi * _x) >> PRECISION;
res += xi * 0x39b78e80;
xi = (xi * _x) >> PRECISION;
res += xi * 0x1fd8080;
xi = (xi * _x) >> PRECISION;
res += xi * 0x10fbc0;
xi = (xi * _x) >> PRECISION;
res += xi * 0x8c40;
xi = (xi * _x) >> PRECISION;
res += xi * 0x462;
xi = (xi * _x) >> PRECISION;
res += xi * 0x22;
return res / 0xde1bc4d19efcac82445da75b00000000;
}
}External Links
Related contracts
Bancor
Same deployerBancor Network Token (BNT), the first Bancor Smart Token, deployed 2017-06-10 ahead of the Bancor crowdsale.
0x1f573d...a7ff1cJune 10, 2017CrowdsaleController
Same deployerThe Bancor crowdsale contract. Opened 2017-06-12 and collected 396,619.89 ETH, about $153M at the time.
0xbbc797...f89710June 10, 2017Ether Token
Same deployerBancor's ETH wrapper token, used as the ETH reserve of the original BNT converter.
0xd76b5c...b746aeJune 22, 2017BancorChanger
Same deployerThe original Bancor converter for BNT, deployed 2017-06-22 with an ETH reserve held as EtherToken.
0xca83bd...629ff4June 22, 2017BancorPriceFloor
Same deployerA contract that bought back BNT for ETH at the 0.01 ETH crowdsale price, deployed 2017-06-25.
0x7ec6a7...268549June 25, 2017BancorGasPriceLimit
Same deployerHolds the maximum gas price allowed for Bancor conversions, set to 20 gwei at deployment on 2017-10-19.
0xafb06c...b99590October 19, 2017