Transaction Hash:
Block:
10670200 at Aug-16-2020 09:04:12 AM +UTC
Transaction Fee:
0.03631030730327971 ETH
$78.64
Gas Used:
266,230 Gas / 136.386986077 Gwei
Emitted Events:
| 41 |
AElfToken.Transfer( from=[Sender] 0x00923b9a074762b93650716333b3e1473a15048e, to=0xb96253C913810238Ab092cca19876D42f4d29cb3, value=6434469350636463232452 )
|
| 42 |
SmartToken.Transfer( _from=0xb96253C913810238Ab092cca19876D42f4d29cb3, _to=LiquidityPoolV1Converter, _value=395096569028874441525 )
|
| 43 |
0xb96253c913810238ab092cca19876d42f4d29cb3.0x276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb( 0x276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb, 0x000000000000000000000000bf2179859fc6d5bee9bf9158632dc51678a4100e, 0x0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c, 0x00000000000000000000000000923b9a074762b93650716333b3e1473a15048e, 00000000000000000000000000000000000000000000015cd033471fc8c695c4, 0000000000000000000000000000000000000000000000156b10fc386da99335, 000000000000000000000000000000000000000000000000057d11ef24c12175 )
|
| 44 |
0xb96253c913810238ab092cca19876d42f4d29cb3.0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24( 0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24, 0x000000000000000000000000bf2179859fc6d5bee9bf9158632dc51678a4100e, 0x0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c, 0000000000000000000000000000000000000000515a66cc42b3cc7041fb7040, 000000000000000000000000000000000000000535f64e5c0c095db31a161900 )
|
| 45 |
0xb96253c913810238ab092cca19876d42f4d29cb3.0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24( 0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24, 0x0000000000000000000000000f2318565f1996cb1ed2f88e172135791bc1fcbf, 0x000000000000000000000000bf2179859fc6d5bee9bf9158632dc51678a4100e, 000000000000000000000000000000000000000a6bec9cb81812bb66342c3200, 0000000000000000000000000000000000000001156fcbe991aa102325961660 )
|
| 46 |
0xb96253c913810238ab092cca19876d42f4d29cb3.0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24( 0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24, 0x0000000000000000000000000f2318565f1996cb1ed2f88e172135791bc1fcbf, 0x0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c, 0000000000000000000000000000000000000000a2b4cd98856798e083f6e080, 0000000000000000000000000000000000000001156fcbe991aa102325961660 )
|
| 47 |
0xb96253c913810238ab092cca19876d42f4d29cb3.0x8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788( 0x8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788, 0x000000000000000000000000bf2179859fc6d5bee9bf9158632dc51678a4100e, 00000000000000000000000000000000000000000000245d3b26b111298d2adb, 00000000000000000000000000000000000000000000aed85778def551032488, 000000000000000000000000000000000000000000000000000000000007a120 )
|
| 48 |
0xb96253c913810238ab092cca19876d42f4d29cb3.0x8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788( 0x8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788, 0x0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c, 00000000000000000000000000000000000000000000245d3b26b111298d2adb, 000000000000000000000000000000000000000000000aa9c213234d203821f2, 000000000000000000000000000000000000000000000000000000000007a120 )
|
| 49 |
BancorNetwork.Conversion( _smartToken=SmartToken, _fromToken=AElfToken, _toToken=SmartToken, _fromAmount=6434469350636463232452, _toAmount=395096569028874441525, _trader=[Sender] 0x00923b9a074762b93650716333b3e1473a15048e )
|
| 50 |
LiquidityPoolV1Converter.Conversion( _fromToken=SmartToken, _toToken=0xEeeeeEee...eeeeeEEeE, _trader=[Sender] 0x00923b9a074762b93650716333b3e1473a15048e, _amount=395096569028874441525, _return=1965633092060043762, _conversionFee=1967600692752796 )
|
| 51 |
LiquidityPoolV1Converter.TokenRateUpdate( _token1=SmartToken, _token2=0xEeeeeEee...eeeeeEEeE, _rateN=9928145643145040540313000000, _rateD=1993780853157975740876574000000 )
|
| 52 |
LiquidityPoolV1Converter.TokenRateUpdate( _token1=SmartToken, _token2=SmartToken, _rateN=3987561706315951481753148000000, _rateD=5885696366553071107328661500000 )
|
| 53 |
LiquidityPoolV1Converter.TokenRateUpdate( _token1=SmartToken, _token2=0xEeeeeEee...eeeeeEEeE, _rateN=19856291286290081080626000000, _rateD=5885696366553071107328661500000 )
|
| 54 |
LiquidityPoolV1Converter.PriceDataUpdate( _connectorToken=SmartToken, _tokenSupply=11771392733106142214657323, _connectorBalance=3987561706315951481753148, _connectorWeight=500000 )
|
| 55 |
LiquidityPoolV1Converter.PriceDataUpdate( _connectorToken=0xEeeeeEee...eeeeeEEeE, _tokenSupply=11771392733106142214657323, _connectorBalance=19856291286290081080626, _connectorWeight=500000 )
|
| 56 |
BancorNetwork.Conversion( _smartToken=SmartToken, _fromToken=SmartToken, _toToken=0xEeeeeEee...eeeeeEEeE, _fromAmount=395096569028874441525, _toAmount=1965633092060043762, _trader=[Sender] 0x00923b9a074762b93650716333b3e1473a15048e )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x00923B9a...73A15048e |
34.380051858425467365 Eth
Nonce: 26443
|
36.309374643182231417 Eth
Nonce: 26444
| 1.929322784756764052 | ||
| 0x1F573D6F...d79a7FF1C | |||||
|
0x829BD824...93333A830
Miner
| (F2Pool Old) | 2,601.159721020192827359 Eth | 2,601.196031327496107069 Eth | 0.03631030730327971 | |
| 0xb96253C9...2f4d29cb3 | (Bancor: Converter 20) | ||||
| 0xbf217985...678a4100e | |||||
| 0xE870D001...E22be4ABd | (Bancor: Converter 216) | 19,858.256919382141124388 Eth | 19,856.291286290081080626 Eth | 1.965633092060043762 |
Execution Trace
BancorNetwork.claimAndConvert2( _path=[0xbf2179859fc6D5BEE9Bf9158632Dc51678a4100e, 0x0F2318565f1996CB1eD2F88e172135791BC1FcBf, 0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C, 0xb1CD6e4153B2a390Cf00A6556b0fC1458C4A5533, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE], _amount=6434469350636463232452, _minReturn=1955466604388854784, _affiliateAccount=0x0000000000000000000000000000000000000000, _affiliateFee=0 ) => ( 1965633092060043762 )
-
SmartToken.CALL( )
-
Bancor: Converter 20.STATICCALL( ) -
AElfToken.transferFrom( _from=0x00923B9a074762B93650716333B3E1473A15048e, _to=0xb96253C913810238Ab092cca19876D42f4d29cb3, _value=6434469350636463232452 ) => ( True )
-
ContractRegistry.addressOf( _contractName=424E54546F6B656E000000000000000000000000000000000000000000000000 ) => ( 0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C ) -
SmartToken.CALL( )
-
Bancor: Converter 20.STATICCALL( ) -
SmartToken.CALL( )
-
LiquidityPoolV1Converter.STATICCALL( ) Bancor: Converter 20.e8dc12ff( )-
ContractRegistry.addressOf( _contractName=42616E636F724E6574776F726B00000000000000000000000000000000000000 ) => ( 0x2F9EC37d6CcFFf1caB21733BdaDEdE11c823cCB0 ) -
SmartToken.CALL( )
-
ContractRegistry.addressOf( _contractName=42616E636F72466F726D756C6100000000000000000000000000000000000000 ) => ( 0xA049894d5dcaD406b7C827D6dc6A0B58CA4AE73a ) -
BancorFormula.crossReserveTargetAmount( _sourceReserveBalance=819248098421426779623108, _sourceReserveWeight=500000, _targetReserveBalance=50750245706264316327207, _targetReserveWeight=500000, _amount=6434469350636463232452 ) => ( 395492061089964405930 ) -
AElfToken.balanceOf( _owner=0xb96253C913810238Ab092cca19876D42f4d29cb3 ) => ( balance=825682567772063242855560 )
-
AElfToken.balanceOf( _owner=0xb96253C913810238Ab092cca19876D42f4d29cb3 ) => ( balance=825682567772063242855560 )
-
SmartToken.transfer( _to=0xE870D00176b2C71AFD4c43ceA550228E22be4ABd, _value=395096569028874441525 ) => ( success=True )
-
SmartToken.CALL( )
-
LiquidityPoolV1Converter.convert( _sourceToken=0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C, _targetToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, _amount=395096569028874441525, _trader=0x00923B9a074762B93650716333B3E1473A15048e, _beneficiary=0x00923B9a074762B93650716333B3E1473A15048e ) => ( 1965633092060043762 )-
ContractRegistry.addressOf( _contractName=42616E636F724E6574776F726B00000000000000000000000000000000000000 ) => ( 0x2F9EC37d6CcFFf1caB21733BdaDEdE11c823cCB0 ) -
SmartToken.CALL( )
-
ContractRegistry.addressOf( _contractName=42616E636F72466F726D756C6100000000000000000000000000000000000000 ) => ( 0xA049894d5dcaD406b7C827D6dc6A0B58CA4AE73a ) -
BancorFormula.crossReserveTargetAmount( _sourceReserveBalance=3987166609746922607311623, _sourceReserveWeight=500000, _targetReserveBalance=19858256919382141124388, _targetReserveWeight=500000, _amount=395096569028874441525 ) => ( 1967600692752796558 ) -
SmartToken.balanceOf( 0xE870D00176b2C71AFD4c43ceA550228E22be4ABd ) => ( 3987561706315951481753148 )
-
SmartToken.balanceOf( 0xE870D00176b2C71AFD4c43ceA550228E22be4ABd ) => ( 3987561706315951481753148 )
- ETH 1.965633092060043762
0x00923b9a074762b93650716333b3e1473a15048e.CALL( ) -
SmartToken.CALL( )
-
claimAndConvert2[BancorNetwork (ln:1484)]
convertByPath[BancorNetwork (ln:1494)]handleSourceToken[BancorNetwork (ln:961)]owner[BancorNetwork (ln:1166)]isV28OrHigherConverter[BancorNetwork (ln:1167)]encodeWithSelector[BancorNetwork (ln:1401)]
value[BancorNetwork (ln:1178)]getConverterEtherTokenAddress[BancorNetwork (ln:1178)]connectorTokenCount[BancorNetwork (ln:1348)]connectorTokens[BancorNetwork (ln:1350)]
safeTransferFrom[BancorNetwork (ln:1184)]withdraw[BancorNetwork (ln:1188)]safeTransferFrom[BancorNetwork (ln:1195)]safeTransferFrom[BancorNetwork (ln:1197)]
createConversionData[BancorNetwork (ln:979)]addressOf[BancorNetwork (ln:1244)]owner[BancorNetwork (ln:1249)]ConversionStep[BancorNetwork (ln:1257)]isV28OrHigherConverter[BancorNetwork (ln:1272)]encodeWithSelector[BancorNetwork (ln:1401)]
getConverterEtherTokenAddress[BancorNetwork (ln:1286)]connectorTokenCount[BancorNetwork (ln:1348)]connectorTokens[BancorNetwork (ln:1350)]
getConverterEtherTokenAddress[BancorNetwork (ln:1297)]connectorTokenCount[BancorNetwork (ln:1348)]connectorTokens[BancorNetwork (ln:1350)]
doConversion[BancorNetwork (ln:980)]safeTransfer[BancorNetwork (ln:1124)]ensureAllowance[BancorNetwork (ln:1130)]allowance[BancorNetwork (ln:1338)]safeApprove[BancorNetwork (ln:1341)]safeApprove[BancorNetwork (ln:1342)]
change[BancorNetwork (ln:1135)]value[BancorNetwork (ln:1137)]convert[BancorNetwork (ln:1139)]div[BancorNetwork (ln:1143)]mul[BancorNetwork (ln:1143)]transfer[BancorNetwork (ln:1144)]Conversion[BancorNetwork (ln:1148)]
handleTargetToken[BancorNetwork (ln:983)]withdrawTo[BancorNetwork (ln:1223)]safeTransfer[BancorNetwork (ln:1227)]
File 1 of 8: BancorNetwork
File 2 of 8: AElfToken
File 3 of 8: LiquidityPoolV1Converter
File 4 of 8: SmartToken
File 5 of 8: SmartToken
File 6 of 8: ContractRegistry
File 7 of 8: SmartToken
File 8 of 8: BancorFormula
// File: contracts/token/interfaces/IERC20Token.sol
pragma solidity 0.4.26;
/*
ERC20 Standard Token interface
*/
contract IERC20Token {
// these functions aren't abstract since the compiler emits automatically generated getter functions as external
function name() public view returns (string) {this;}
function symbol() public view returns (string) {this;}
function decimals() public view returns (uint8) {this;}
function totalSupply() public view returns (uint256) {this;}
function balanceOf(address _owner) public view returns (uint256) {_owner; this;}
function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;}
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
}
// File: contracts/IBancorNetwork.sol
pragma solidity 0.4.26;
/*
Bancor Network interface
*/
contract IBancorNetwork {
function convert2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _affiliateAccount,
uint256 _affiliateFee
) public payable returns (uint256);
function claimAndConvert2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _affiliateAccount,
uint256 _affiliateFee
) public returns (uint256);
function convertFor2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _for,
address _affiliateAccount,
uint256 _affiliateFee
) public payable returns (uint256);
function claimAndConvertFor2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _for,
address _affiliateAccount,
uint256 _affiliateFee
) public returns (uint256);
// deprecated, backward compatibility
function convert(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn
) public payable returns (uint256);
// deprecated, backward compatibility
function claimAndConvert(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn
) public returns (uint256);
// deprecated, backward compatibility
function convertFor(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _for
) public payable returns (uint256);
// deprecated, backward compatibility
function claimAndConvertFor(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _for
) public returns (uint256);
}
// File: contracts/IConversionPathFinder.sol
pragma solidity 0.4.26;
/*
Conversion Path Finder interface
*/
contract IConversionPathFinder {
function findPath(address _sourceToken, address _targetToken) public view returns (address[] memory);
}
// File: contracts/utility/interfaces/IOwned.sol
pragma solidity 0.4.26;
/*
Owned contract interface
*/
contract IOwned {
// this function isn't abstract since the compiler emits automatically generated getter functions as external
function owner() public view returns (address) {this;}
function transferOwnership(address _newOwner) public;
function acceptOwnership() public;
}
// File: contracts/utility/interfaces/ITokenHolder.sol
pragma solidity 0.4.26;
/*
Token Holder interface
*/
contract ITokenHolder is IOwned {
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
}
// File: contracts/converter/interfaces/IConverterAnchor.sol
pragma solidity 0.4.26;
/*
Converter Anchor interface
*/
contract IConverterAnchor is IOwned, ITokenHolder {
}
// File: contracts/utility/interfaces/IWhitelist.sol
pragma solidity 0.4.26;
/*
Whitelist interface
*/
contract IWhitelist {
function isWhitelisted(address _address) public view returns (bool);
}
// File: contracts/converter/interfaces/IConverter.sol
pragma solidity 0.4.26;
/*
Converter interface
*/
contract IConverter is IOwned {
function converterType() public pure returns (uint16);
function anchor() public view returns (IConverterAnchor) {this;}
function isActive() public view returns (bool);
function rateAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view returns (uint256, uint256);
function convert(IERC20Token _sourceToken,
IERC20Token _targetToken,
uint256 _amount,
address _trader,
address _beneficiary) public payable returns (uint256);
function conversionWhitelist() public view returns (IWhitelist) {this;}
function conversionFee() public view returns (uint32) {this;}
function maxConversionFee() public view returns (uint32) {this;}
function reserveBalance(IERC20Token _reserveToken) public view returns (uint256);
function() external payable;
function transferAnchorOwnership(address _newOwner) public;
function acceptAnchorOwnership() public;
function setConversionFee(uint32 _conversionFee) public;
function setConversionWhitelist(IWhitelist _whitelist) public;
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
function withdrawETH(address _to) public;
function addReserve(IERC20Token _token, uint32 _ratio) public;
// deprecated, backward compatibility
function token() public view returns (IConverterAnchor);
function transferTokenOwnership(address _newOwner) public;
function acceptTokenOwnership() public;
function connectors(address _address) public view returns (uint256, uint32, bool, bool, bool);
function getConnectorBalance(IERC20Token _connectorToken) public view returns (uint256);
function connectorTokens(uint256 _index) public view returns (IERC20Token);
function connectorTokenCount() public view returns (uint16);
}
// File: contracts/converter/interfaces/IBancorFormula.sol
pragma solidity 0.4.26;
/*
Bancor Formula interface
*/
contract IBancorFormula {
function purchaseRate(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256);
function saleRate(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256);
function crossReserveRate(uint256 _sourceReserveBalance,
uint32 _sourceReserveWeight,
uint256 _targetReserveBalance,
uint32 _targetReserveWeight,
uint256 _amount)
public view returns (uint256);
function fundCost(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256);
function liquidateRate(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256);
}
// File: contracts/utility/Owned.sol
pragma solidity 0.4.26;
/**
* @dev Provides support and utilities for contract ownership
*/
contract Owned is IOwned {
address public owner;
address public newOwner;
/**
* @dev triggered when the owner is updated
*
* @param _prevOwner previous owner
* @param _newOwner new owner
*/
event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
/**
* @dev initializes a new Owned instance
*/
constructor() public {
owner = msg.sender;
}
// allows execution by the owner only
modifier ownerOnly {
_ownerOnly();
_;
}
// error message binary size optimization
function _ownerOnly() internal view {
require(msg.sender == owner, "ERR_ACCESS_DENIED");
}
/**
* @dev allows transferring the contract ownership
* the new owner still needs to accept the transfer
* can only be called by the contract owner
*
* @param _newOwner new contract owner
*/
function transferOwnership(address _newOwner) public ownerOnly {
require(_newOwner != owner, "ERR_SAME_OWNER");
newOwner = _newOwner;
}
/**
* @dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() public {
require(msg.sender == newOwner, "ERR_ACCESS_DENIED");
emit OwnerUpdate(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
// File: contracts/utility/Utils.sol
pragma solidity 0.4.26;
/**
* @dev Utilities & Common Modifiers
*/
contract Utils {
// verifies that a value is greater than zero
modifier greaterThanZero(uint256 _value) {
_greaterThanZero(_value);
_;
}
// error message binary size optimization
function _greaterThanZero(uint256 _value) internal pure {
require(_value > 0, "ERR_ZERO_VALUE");
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address _address) {
_validAddress(_address);
_;
}
// error message binary size optimization
function _validAddress(address _address) internal pure {
require(_address != address(0), "ERR_INVALID_ADDRESS");
}
// verifies that the address is different than this contract address
modifier notThis(address _address) {
_notThis(_address);
_;
}
// error message binary size optimization
function _notThis(address _address) internal view {
require(_address != address(this), "ERR_ADDRESS_IS_SELF");
}
}
// File: contracts/utility/interfaces/IContractRegistry.sol
pragma solidity 0.4.26;
/*
Contract Registry interface
*/
contract IContractRegistry {
function addressOf(bytes32 _contractName) public view returns (address);
// deprecated, backward compatibility
function getAddress(bytes32 _contractName) public view returns (address);
}
// File: contracts/utility/ContractRegistryClient.sol
pragma solidity 0.4.26;
/**
* @dev Base contract for ContractRegistry clients
*/
contract ContractRegistryClient is Owned, Utils {
bytes32 internal constant CONTRACT_REGISTRY = "ContractRegistry";
bytes32 internal constant BANCOR_NETWORK = "BancorNetwork";
bytes32 internal constant BANCOR_FORMULA = "BancorFormula";
bytes32 internal constant CONVERTER_FACTORY = "ConverterFactory";
bytes32 internal constant CONVERSION_PATH_FINDER = "ConversionPathFinder";
bytes32 internal constant CONVERTER_UPGRADER = "BancorConverterUpgrader";
bytes32 internal constant CONVERTER_REGISTRY = "BancorConverterRegistry";
bytes32 internal constant CONVERTER_REGISTRY_DATA = "BancorConverterRegistryData";
bytes32 internal constant BNT_TOKEN = "BNTToken";
bytes32 internal constant BANCOR_X = "BancorX";
bytes32 internal constant BANCOR_X_UPGRADER = "BancorXUpgrader";
IContractRegistry public registry; // address of the current contract-registry
IContractRegistry public prevRegistry; // address of the previous contract-registry
bool public onlyOwnerCanUpdateRegistry; // only an owner can update the contract-registry
/**
* @dev verifies that the caller is mapped to the given contract name
*
* @param _contractName contract name
*/
modifier only(bytes32 _contractName) {
_only(_contractName);
_;
}
// error message binary size optimization
function _only(bytes32 _contractName) internal view {
require(msg.sender == addressOf(_contractName), "ERR_ACCESS_DENIED");
}
/**
* @dev initializes a new ContractRegistryClient instance
*
* @param _registry address of a contract-registry contract
*/
constructor(IContractRegistry _registry) internal validAddress(_registry) {
registry = IContractRegistry(_registry);
prevRegistry = IContractRegistry(_registry);
}
/**
* @dev updates to the new contract-registry
*/
function updateRegistry() public {
// verify that this function is permitted
require(msg.sender == owner || !onlyOwnerCanUpdateRegistry, "ERR_ACCESS_DENIED");
// get the new contract-registry
IContractRegistry newRegistry = IContractRegistry(addressOf(CONTRACT_REGISTRY));
// verify that the new contract-registry is different and not zero
require(newRegistry != address(registry) && newRegistry != address(0), "ERR_INVALID_REGISTRY");
// verify that the new contract-registry is pointing to a non-zero contract-registry
require(newRegistry.addressOf(CONTRACT_REGISTRY) != address(0), "ERR_INVALID_REGISTRY");
// save a backup of the current contract-registry before replacing it
prevRegistry = registry;
// replace the current contract-registry with the new contract-registry
registry = newRegistry;
}
/**
* @dev restores the previous contract-registry
*/
function restoreRegistry() public ownerOnly {
// restore the previous contract-registry
registry = prevRegistry;
}
/**
* @dev restricts the permission to update the contract-registry
*
* @param _onlyOwnerCanUpdateRegistry indicates whether or not permission is restricted to owner only
*/
function restrictRegistryUpdate(bool _onlyOwnerCanUpdateRegistry) public ownerOnly {
// change the permission to update the contract-registry
onlyOwnerCanUpdateRegistry = _onlyOwnerCanUpdateRegistry;
}
/**
* @dev returns the address associated with the given contract name
*
* @param _contractName contract name
*
* @return contract address
*/
function addressOf(bytes32 _contractName) internal view returns (address) {
return registry.addressOf(_contractName);
}
}
// File: contracts/utility/ReentrancyGuard.sol
pragma solidity 0.4.26;
/**
* @dev ReentrancyGuard
*
* The contract provides protection against re-entrancy - calling a function (directly or
* indirectly) from within itself.
*/
contract ReentrancyGuard {
// true while protected code is being executed, false otherwise
bool private locked = false;
/**
* @dev ensures instantiation only by sub-contracts
*/
constructor() internal {}
// protects a function against reentrancy attacks
modifier protected() {
_protected();
locked = true;
_;
locked = false;
}
// error message binary size optimization
function _protected() internal view {
require(!locked, "ERR_REENTRANCY");
}
}
// File: contracts/utility/TokenHandler.sol
pragma solidity 0.4.26;
contract TokenHandler {
bytes4 private constant APPROVE_FUNC_SELECTOR = bytes4(keccak256("approve(address,uint256)"));
bytes4 private constant TRANSFER_FUNC_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
bytes4 private constant TRANSFER_FROM_FUNC_SELECTOR = bytes4(keccak256("transferFrom(address,address,uint256)"));
/**
* @dev executes the ERC20 token's `approve` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _spender approved address
* @param _value allowance amount
*/
function safeApprove(IERC20Token _token, address _spender, uint256 _value) internal {
execute(_token, abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value));
}
/**
* @dev executes the ERC20 token's `transfer` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _to target address
* @param _value transfer amount
*/
function safeTransfer(IERC20Token _token, address _to, uint256 _value) internal {
execute(_token, abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value));
}
/**
* @dev executes the ERC20 token's `transferFrom` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _from source address
* @param _to target address
* @param _value transfer amount
*/
function safeTransferFrom(IERC20Token _token, address _from, address _to, uint256 _value) internal {
execute(_token, abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value));
}
/**
* @dev executes a function on the ERC20 token and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _data data to pass in to the token's contract for execution
*/
function execute(IERC20Token _token, bytes memory _data) private {
uint256[1] memory ret = [uint256(1)];
assembly {
let success := call(
gas, // gas remaining
_token, // destination address
0, // no ether
add(_data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
mload(_data), // input length (loaded from the first 32 bytes in the `data` array)
ret, // output buffer
32 // output length
)
if iszero(success) {
revert(0, 0)
}
}
require(ret[0] != 0, "ERR_TRANSFER_FAILED");
}
}
// File: contracts/utility/TokenHolder.sol
pragma solidity 0.4.26;
/**
* @dev We consider every contract to be a 'token holder' since it's currently not possible
* for a contract to deny receiving tokens.
*
* The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
* the owner to send tokens that were sent to the contract by mistake back to their sender.
*
* Note that we use the non standard ERC-20 interface which has no return value for transfer
* in order to support both non standard as well as standard token contracts.
* see https://github.com/ethereum/solidity/issues/4116
*/
contract TokenHolder is ITokenHolder, TokenHandler, Owned, Utils {
/**
* @dev withdraws tokens held by the contract and sends them to an account
* can only be called by the owner
*
* @param _token ERC20 token contract address
* @param _to account to receive the new amount
* @param _amount amount to withdraw
*/
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
public
ownerOnly
validAddress(_token)
validAddress(_to)
notThis(_to)
{
safeTransfer(_token, _to, _amount);
}
}
// File: contracts/utility/SafeMath.sol
pragma solidity 0.4.26;
/**
* @dev Library for basic math operations with overflow/underflow protection
*/
library SafeMath {
/**
* @dev returns the sum of _x and _y, reverts if the calculation overflows
*
* @param _x value 1
* @param _y value 2
*
* @return sum
*/
function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
uint256 z = _x + _y;
require(z >= _x, "ERR_OVERFLOW");
return z;
}
/**
* @dev returns the difference of _x minus _y, reverts if the calculation underflows
*
* @param _x minuend
* @param _y subtrahend
*
* @return difference
*/
function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_x >= _y, "ERR_UNDERFLOW");
return _x - _y;
}
/**
* @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
*
* @param _x factor 1
* @param _y factor 2
*
* @return product
*/
function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
// gas optimization
if (_x == 0)
return 0;
uint256 z = _x * _y;
require(z / _x == _y, "ERR_OVERFLOW");
return z;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*
* @param _x dividend
* @param _y divisor
*
* @return quotient
*/
function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_y > 0, "ERR_DIVIDE_BY_ZERO");
uint256 c = _x / _y;
return c;
}
}
// File: contracts/token/interfaces/IEtherToken.sol
pragma solidity 0.4.26;
/*
Ether Token interface
*/
contract IEtherToken is IERC20Token {
function deposit() public payable;
function withdraw(uint256 _amount) public;
function depositTo(address _to) public payable;
function withdrawTo(address _to, uint256 _amount) public;
}
// File: contracts/token/interfaces/ISmartToken.sol
pragma solidity 0.4.26;
/*
Smart Token interface
*/
contract ISmartToken is IConverterAnchor, IERC20Token {
function disableTransfers(bool _disable) public;
function issue(address _to, uint256 _amount) public;
function destroy(address _from, uint256 _amount) public;
}
// File: contracts/bancorx/interfaces/IBancorX.sol
pragma solidity 0.4.26;
contract IBancorX {
function token() public view returns (IERC20Token) {this;}
function xTransfer(bytes32 _toBlockchain, bytes32 _to, uint256 _amount, uint256 _id) public;
function getXTransferAmount(uint256 _xTransferId, address _for) public view returns (uint256);
}
// File: contracts/BancorNetwork.sol
pragma solidity 0.4.26;
// interface of older converters for backward compatibility
contract ILegacyConverter {
function change(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, uint256 _minReturn) public returns (uint256);
}
/**
* @dev The BancorNetwork contract is the main entry point for Bancor token conversions.
* It also allows for the conversion of any token in the Bancor Network to any other token in a single
* transaction by providing a conversion path.
*
* A note on Conversion Path: Conversion path is a data structure that is used when converting a token
* to another token in the Bancor Network, when the conversion cannot necessarily be done by a single
* converter and might require multiple 'hops'.
* The path defines which converters should be used and what kind of conversion should be done in each step.
*
* The path format doesn't include complex structure; instead, it is represented by a single array
* in which each 'hop' is represented by a 2-tuple - converter anchor & target token.
* In addition, the first element is always the source token.
* The converter anchor is only used as a pointer to a converter (since converter addresses are more
* likely to change as opposed to anchor addresses).
*
* Format:
* [source token, converter anchor, target token, converter anchor, target token...]
*/
contract BancorNetwork is IBancorNetwork, TokenHolder, ContractRegistryClient, ReentrancyGuard {
using SafeMath for uint256;
uint256 private constant CONVERSION_FEE_RESOLUTION = 1000000;
uint256 private constant AFFILIATE_FEE_RESOLUTION = 1000000;
address private constant ETH_RESERVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
struct ConversionStep {
IConverter converter;
IConverterAnchor anchor;
IERC20Token sourceToken;
IERC20Token targetToken;
address beneficiary;
bool isV28OrHigherConverter;
bool processAffiliateFee;
}
uint256 public maxAffiliateFee = 30000; // maximum affiliate-fee
mapping (address => bool) public etherTokens; // list of all supported ether tokens
/**
* @dev triggered when a conversion between two tokens occurs
*
* @param _smartToken anchor governed by the converter
* @param _fromToken source ERC20 token
* @param _toToken target ERC20 token
* @param _fromAmount amount converted, in the source token
* @param _toAmount amount returned, minus conversion fee
* @param _trader wallet that initiated the trade
*/
event Conversion(
address indexed _smartToken,
address indexed _fromToken,
address indexed _toToken,
uint256 _fromAmount,
uint256 _toAmount,
address _trader
);
/**
* @dev initializes a new BancorNetwork instance
*
* @param _registry address of a contract registry contract
*/
constructor(IContractRegistry _registry) ContractRegistryClient(_registry) public {
etherTokens[ETH_RESERVE_ADDRESS] = true;
}
/**
* @dev allows the owner to update the maximum affiliate-fee
*
* @param _maxAffiliateFee maximum affiliate-fee
*/
function setMaxAffiliateFee(uint256 _maxAffiliateFee)
public
ownerOnly
{
require(_maxAffiliateFee <= AFFILIATE_FEE_RESOLUTION, "ERR_INVALID_AFFILIATE_FEE");
maxAffiliateFee = _maxAffiliateFee;
}
/**
* @dev allows the owner to register/unregister ether tokens
*
* @param _token ether token contract address
* @param _register true to register, false to unregister
*/
function registerEtherToken(IEtherToken _token, bool _register)
public
ownerOnly
validAddress(_token)
notThis(_token)
{
etherTokens[_token] = _register;
}
/**
* @dev returns the conversion path between two tokens in the network
* note that this method is quite expensive in terms of gas and should generally be called off-chain
*
* @param _sourceToken source token address
* @param _targetToken target token address
*
* @return conversion path between the two tokens
*/
function conversionPath(IERC20Token _sourceToken, IERC20Token _targetToken) public view returns (address[]) {
IConversionPathFinder pathFinder = IConversionPathFinder(addressOf(CONVERSION_PATH_FINDER));
return pathFinder.findPath(_sourceToken, _targetToken);
}
/**
* @dev returns the expected rate of converting a given amount on a given path
* note that there is no support for circular paths
*
* @param _path conversion path (see conversion path format above)
* @param _amount amount of _path[0] tokens received from the sender
*
* @return expected rate
*/
function rateByPath(IERC20Token[] _path, uint256 _amount) public view returns (uint256) {
uint256 amount;
uint256 fee;
uint256 supply;
uint256 balance;
uint32 weight;
IConverter converter;
IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA));
amount = _amount;
// verify that the number of elements is larger than 2 and odd
require(_path.length > 2 && _path.length % 2 == 1, "ERR_INVALID_PATH");
// iterate over the conversion path
for (uint256 i = 2; i < _path.length; i += 2) {
IERC20Token sourceToken = _path[i - 2];
IERC20Token anchor = _path[i - 1];
IERC20Token targetToken = _path[i];
converter = IConverter(IConverterAnchor(anchor).owner());
// backward compatibility
sourceToken = getConverterTokenAddress(converter, sourceToken);
targetToken = getConverterTokenAddress(converter, targetToken);
if (targetToken == anchor) { // buy the smart token
// check if the current smart token has changed
if (i < 3 || anchor != _path[i - 3])
supply = ISmartToken(anchor).totalSupply();
// get the amount & the conversion fee
balance = converter.getConnectorBalance(sourceToken);
(, weight, , , ) = converter.connectors(sourceToken);
amount = formula.purchaseRate(supply, balance, weight, amount);
fee = amount.mul(converter.conversionFee()).div(CONVERSION_FEE_RESOLUTION);
amount -= fee;
// update the smart token supply for the next iteration
supply = supply.add(amount);
}
else if (sourceToken == anchor) { // sell the smart token
// check if the current smart token has changed
if (i < 3 || anchor != _path[i - 3])
supply = ISmartToken(anchor).totalSupply();
// get the amount & the conversion fee
balance = converter.getConnectorBalance(targetToken);
(, weight, , , ) = converter.connectors(targetToken);
amount = formula.saleRate(supply, balance, weight, amount);
fee = amount.mul(converter.conversionFee()).div(CONVERSION_FEE_RESOLUTION);
amount -= fee;
// update the smart token supply for the next iteration
supply = supply.sub(amount);
}
else { // cross reserve conversion
(amount, fee) = getReturn(converter, sourceToken, targetToken, amount);
}
}
return amount;
}
/**
* @dev converts the token to any other token in the bancor network by following
* a predefined conversion path and transfers the result tokens to a target account
* affiliate account/fee can also be passed in to receive a conversion fee (on top of the liquidity provider fees)
* note that the network should already have been given allowance of the source token (if not ETH)
*
* @param _path conversion path, see conversion path format above
* @param _amount amount to convert from, in the source token
* @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be greater than zero
* @param _beneficiary account that will receive the conversion result or 0x0 to send the result to the sender account
* @param _affiliateAccount wallet address to receive the affiliate fee or 0x0 to disable affiliate fee
* @param _affiliateFee affiliate fee in PPM or 0 to disable affiliate fee
*
* @return amount of tokens received from the conversion
*/
function convertByPath(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _beneficiary, address _affiliateAccount, uint256 _affiliateFee)
public
payable
protected
greaterThanZero(_minReturn)
returns (uint256)
{
// verify that the path contrains at least a single 'hop' and that the number of elements is odd
require(_path.length > 2 && _path.length % 2 == 1, "ERR_INVALID_PATH");
// validate msg.value and prepare the source token for the conversion
handleSourceToken(_path[0], IConverterAnchor(_path[1]), _amount);
// check if affiliate fee is enabled
bool affiliateFeeEnabled = false;
if (address(_affiliateAccount) == 0) {
require(_affiliateFee == 0, "ERR_INVALID_AFFILIATE_FEE");
}
else {
require(0 < _affiliateFee && _affiliateFee <= maxAffiliateFee, "ERR_INVALID_AFFILIATE_FEE");
affiliateFeeEnabled = true;
}
// check if beneficiary is set
address beneficiary = msg.sender;
if (_beneficiary != address(0))
beneficiary = _beneficiary;
// convert and get the resulting amount
ConversionStep[] memory data = createConversionData(_path, beneficiary, affiliateFeeEnabled);
uint256 amount = doConversion(data, _amount, _minReturn, _affiliateAccount, _affiliateFee);
// handle the conversion target tokens
handleTargetToken(data, amount, beneficiary);
return amount;
}
/**
* @dev converts any other token to BNT in the bancor network by following
a predefined conversion path and transfers the result to an account on a different blockchain
* note that the network should already have been given allowance of the source token (if not ETH)
*
* @param _path conversion path, see conversion path format above
* @param _amount amount to convert from, in the source token
* @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be greater than zero
* @param _targetBlockchain blockchain BNT will be issued on
* @param _targetAccount address/account on the target blockchain to send the BNT to
* @param _conversionId pre-determined unique (if non zero) id which refers to this transaction
*
* @return the amount of BNT received from this conversion
*/
function xConvert(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
bytes32 _targetBlockchain,
bytes32 _targetAccount,
uint256 _conversionId
)
public
payable
returns (uint256)
{
return xConvert2(_path, _amount, _minReturn, _targetBlockchain, _targetAccount, _conversionId, address(0), 0);
}
/**
* @dev converts any other token to BNT in the bancor network by following
a predefined conversion path and transfers the result to an account on a different blockchain
* note that the network should already have been given allowance of the source token (if not ETH)
*
* @param _path conversion path, see conversion path format above
* @param _amount amount to convert from, in the source token
* @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be greater than zero
* @param _targetBlockchain blockchain BNT will be issued on
* @param _targetAccount address/account on the target blockchain to send the BNT to
* @param _conversionId pre-determined unique (if non zero) id which refers to this transaction
* @param _affiliateAccount affiliate account
* @param _affiliateFee affiliate fee in PPM
*
* @return the amount of BNT received from this conversion
*/
function xConvert2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
bytes32 _targetBlockchain,
bytes32 _targetAccount,
uint256 _conversionId,
address _affiliateAccount,
uint256 _affiliateFee
)
public
payable
greaterThanZero(_minReturn)
returns (uint256)
{
IERC20Token targetToken = _path[_path.length - 1];
IBancorX bancorX = IBancorX(addressOf(BANCOR_X));
// verify that the destination token is BNT
require(targetToken == addressOf(BNT_TOKEN), "ERR_INVALID_TARGET_TOKEN");
// convert and get the resulting amount
uint256 amount = convertByPath(_path, _amount, _minReturn, this, _affiliateAccount, _affiliateFee);
// grant BancorX allowance
ensureAllowance(targetToken, bancorX, amount);
// transfer the resulting amount to BancorX
bancorX.xTransfer(_targetBlockchain, _targetAccount, amount, _conversionId);
return amount;
}
/**
* @dev allows a user to convert a token that was sent from another blockchain into any other
* token on the BancorNetwork
* ideally this transaction is created before the previous conversion is even complete, so
* so the input amount isn't known at that point - the amount is actually take from the
* BancorX contract directly by specifying the conversion id
*
* @param _path conversion path
* @param _bancorX address of the BancorX contract for the source token
* @param _conversionId pre-determined unique (if non zero) id which refers to this conversion
* @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero
* @param _beneficiary wallet to receive the conversion result
*
* @return amount of tokens received from the conversion
*/
function completeXConversion(IERC20Token[] _path, IBancorX _bancorX, uint256 _conversionId, uint256 _minReturn, address _beneficiary)
public returns (uint256)
{
// verify that the source token is the BancorX token
require(_path[0] == _bancorX.token(), "ERR_INVALID_SOURCE_TOKEN");
// get conversion amount from BancorX contract
uint256 amount = _bancorX.getXTransferAmount(_conversionId, msg.sender);
// perform the conversion
return convertByPath(_path, amount, _minReturn, _beneficiary, address(0), 0);
}
/**
* @dev executes the actual conversion by following the conversion path
*
* @param _data conversion data, see ConversionStep struct above
* @param _amount amount to convert from, in the source token
* @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be greater than zero
* @param _affiliateAccount affiliate account
* @param _affiliateFee affiliate fee in PPM
*
* @return amount of tokens received from the conversion
*/
function doConversion(
ConversionStep[] _data,
uint256 _amount,
uint256 _minReturn,
address _affiliateAccount,
uint256 _affiliateFee
) private returns (uint256) {
uint256 toAmount;
uint256 fromAmount = _amount;
// iterate over the conversion data
for (uint256 i = 0; i < _data.length; i++) {
ConversionStep memory stepData = _data[i];
// newer converter
if (stepData.isV28OrHigherConverter) {
// transfer the tokens to the converter only if the network contract currently holds the tokens
// not needed with ETH or if it's the first conversion step
if (i != 0 && _data[i - 1].beneficiary == address(this) && !etherTokens[stepData.sourceToken])
safeTransfer(stepData.sourceToken, stepData.converter, fromAmount);
}
// older converter
// if the source token is the smart token, no need to do any transfers as the converter controls it
else if (stepData.sourceToken != ISmartToken(stepData.anchor)) {
// grant allowance for it to transfer the tokens from the network contract
ensureAllowance(stepData.sourceToken, stepData.converter, fromAmount);
}
// do the conversion
if (!stepData.isV28OrHigherConverter)
toAmount = ILegacyConverter(stepData.converter).change(stepData.sourceToken, stepData.targetToken, fromAmount, 1);
else if (etherTokens[stepData.sourceToken])
toAmount = stepData.converter.convert.value(msg.value)(stepData.sourceToken, stepData.targetToken, fromAmount, msg.sender, stepData.beneficiary);
else
toAmount = stepData.converter.convert(stepData.sourceToken, stepData.targetToken, fromAmount, msg.sender, stepData.beneficiary);
// pay affiliate-fee if needed
if (stepData.processAffiliateFee) {
uint256 affiliateAmount = toAmount.mul(_affiliateFee).div(AFFILIATE_FEE_RESOLUTION);
require(stepData.targetToken.transfer(_affiliateAccount, affiliateAmount), "ERR_FEE_TRANSFER_FAILED");
toAmount -= affiliateAmount;
}
emit Conversion(stepData.anchor, stepData.sourceToken, stepData.targetToken, fromAmount, toAmount, msg.sender);
fromAmount = toAmount;
}
// ensure the trade meets the minimum requested amount
require(toAmount >= _minReturn, "ERR_RETURN_TOO_LOW");
return toAmount;
}
/**
* @dev validates msg.value and prepares the conversion source token for the conversion
*
* @param _sourceToken source token of the first conversion step
* @param _anchor converter anchor of the first conversion step
* @param _amount amount to convert from, in the source token
*/
function handleSourceToken(IERC20Token _sourceToken, IConverterAnchor _anchor, uint256 _amount) private {
IConverter firstConverter = IConverter(_anchor.owner());
bool isNewerConverter = isV28OrHigherConverter(firstConverter);
// ETH
if (msg.value > 0) {
// validate msg.value
require(msg.value == _amount, "ERR_ETH_AMOUNT_MISMATCH");
// EtherToken converter - deposit the ETH into the EtherToken
// note that it can still be a non ETH converter if the path is wrong
// but such conversion will simply revert
if (!isNewerConverter)
IEtherToken(getConverterEtherTokenAddress(firstConverter)).deposit.value(msg.value)();
}
// EtherToken
else if (etherTokens[_sourceToken]) {
// claim the tokens - if the source token is ETH reserve, this call will fail
// since in that case the transaction must be sent with msg.value
safeTransferFrom(_sourceToken, msg.sender, this, _amount);
// ETH converter - withdraw the ETH
if (isNewerConverter)
IEtherToken(_sourceToken).withdraw(_amount);
}
// other ERC20 token
else {
// newer converter - transfer the tokens from the sender directly to the converter
// otherwise claim the tokens
if (isNewerConverter)
safeTransferFrom(_sourceToken, msg.sender, firstConverter, _amount);
else
safeTransferFrom(_sourceToken, msg.sender, this, _amount);
}
}
/**
* @dev handles the conversion target token if the network still holds it at the end of the conversion
*
* @param _data conversion data, see ConversionStep struct above
* @param _amount conversion return amount, in the target token
* @param _beneficiary wallet to receive the conversion result
*/
function handleTargetToken(ConversionStep[] _data, uint256 _amount, address _beneficiary) private {
ConversionStep memory stepData = _data[_data.length - 1];
// network contract doesn't hold the tokens, do nothing
if (stepData.beneficiary != address(this))
return;
IERC20Token targetToken = stepData.targetToken;
// ETH / EtherToken
if (etherTokens[targetToken]) {
// newer converter should send ETH directly to the beneficiary
assert(!stepData.isV28OrHigherConverter);
// EtherToken converter - withdraw the ETH and transfer to the beneficiary
IEtherToken(targetToken).withdrawTo(_beneficiary, _amount);
}
// other ERC20 token
else {
safeTransfer(targetToken, _beneficiary, _amount);
}
}
/**
* @dev creates a memory cache of all conversion steps data to minimize logic and external calls during conversions
*
* @param _conversionPath conversion path, see conversion path format above
* @param _beneficiary wallet to receive the conversion result
* @param _affiliateFeeEnabled true if affiliate fee was requested by the sender, false if not
*
* @return cached conversion data to be ingested later on by the conversion flow
*/
function createConversionData(IERC20Token[] _conversionPath, address _beneficiary, bool _affiliateFeeEnabled) private view returns (ConversionStep[]) {
ConversionStep[] memory data = new ConversionStep[](_conversionPath.length / 2);
bool affiliateFeeProcessed = false;
address bntToken = addressOf(BNT_TOKEN);
// iterate the conversion path and create the conversion data for each step
uint256 i;
for (i = 0; i < _conversionPath.length - 1; i += 2) {
IConverterAnchor anchor = IConverterAnchor(_conversionPath[i + 1]);
IConverter converter = IConverter(anchor.owner());
IERC20Token targetToken = _conversionPath[i + 2];
// check if the affiliate fee should be processed in this step
bool processAffiliateFee = _affiliateFeeEnabled && !affiliateFeeProcessed && targetToken == bntToken;
if (processAffiliateFee)
affiliateFeeProcessed = true;
data[i / 2] = ConversionStep({
// set the converter anchor
anchor: anchor,
// set the converter
converter: converter,
// set the source/target tokens
sourceToken: _conversionPath[i],
targetToken: targetToken,
// requires knowledge about the next step, so initialize in the next phase
beneficiary: address(0),
// set flags
isV28OrHigherConverter: isV28OrHigherConverter(converter),
processAffiliateFee: processAffiliateFee
});
}
// ETH support
// source is ETH
ConversionStep memory stepData = data[0];
if (etherTokens[stepData.sourceToken]) {
// newer converter - replace the source token address with ETH reserve address
if (stepData.isV28OrHigherConverter)
stepData.sourceToken = IERC20Token(ETH_RESERVE_ADDRESS);
// older converter - replace the source token with the EtherToken address used by the converter
else
stepData.sourceToken = IERC20Token(getConverterEtherTokenAddress(stepData.converter));
}
// target is ETH
stepData = data[data.length - 1];
if (etherTokens[stepData.targetToken]) {
// newer converter - replace the target token address with ETH reserve address
if (stepData.isV28OrHigherConverter)
stepData.targetToken = IERC20Token(ETH_RESERVE_ADDRESS);
// older converter - replace the target token with the EtherToken address used by the converter
else
stepData.targetToken = IERC20Token(getConverterEtherTokenAddress(stepData.converter));
}
// set the beneficiary for each step
for (i = 0; i < data.length; i++) {
stepData = data[i];
// first check if the converter in this step is newer as older converters don't even support the beneficiary argument
if (stepData.isV28OrHigherConverter) {
// if affiliate fee is processed in this step, beneficiary is the network contract
if (stepData.processAffiliateFee)
stepData.beneficiary = this;
// if it's the last step, beneficiary is the final beneficiary
else if (i == data.length - 1)
stepData.beneficiary = _beneficiary;
// if the converter in the next step is newer, beneficiary is the next converter
else if (data[i + 1].isV28OrHigherConverter)
stepData.beneficiary = data[i + 1].converter;
// the converter in the next step is older, beneficiary is the network contract
else
stepData.beneficiary = this;
}
else {
// converter in this step is older, beneficiary is the network contract
stepData.beneficiary = this;
}
}
return data;
}
/**
* @dev utility, checks whether allowance for the given spender exists and approves one if it doesn't.
* Note that we use the non standard erc-20 interface in which `approve` has no return value so that
* this function will work for both standard and non standard tokens
*
* @param _token token to check the allowance in
* @param _spender approved address
* @param _value allowance amount
*/
function ensureAllowance(IERC20Token _token, address _spender, uint256 _value) private {
uint256 allowance = _token.allowance(this, _spender);
if (allowance < _value) {
if (allowance > 0)
safeApprove(_token, _spender, 0);
safeApprove(_token, _spender, _value);
}
}
// legacy - returns the address of an EtherToken used by the converter
function getConverterEtherTokenAddress(IConverter _converter) private view returns (address) {
uint256 reserveCount = _converter.connectorTokenCount();
for (uint256 i = 0; i < reserveCount; i++) {
address reserveTokenAddress = _converter.connectorTokens(i);
if (etherTokens[reserveTokenAddress])
return reserveTokenAddress;
}
return ETH_RESERVE_ADDRESS;
}
// legacy - if the token is an ether token, returns the ETH reserve address
// used by the converter, otherwise returns the input token address
function getConverterTokenAddress(IConverter _converter, IERC20Token _token) private view returns (IERC20Token) {
if (!etherTokens[_token])
return _token;
if (isV28OrHigherConverter(_converter))
return IERC20Token(ETH_RESERVE_ADDRESS);
return IERC20Token(getConverterEtherTokenAddress(_converter));
}
bytes4 private constant GET_RETURN_FUNC_SELECTOR = bytes4(keccak256("getReturn(address,address,uint256)"));
// using assembly code since older converter versions have different return values
function getReturn(address _dest, address _sourceToken, address _targetToken, uint256 _amount) internal view returns (uint256, uint256) {
uint256[2] memory ret;
bytes memory data = abi.encodeWithSelector(GET_RETURN_FUNC_SELECTOR, _sourceToken, _targetToken, _amount);
assembly {
let success := staticcall(
gas, // gas remaining
_dest, // destination address
add(data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
mload(data), // input length (loaded from the first 32 bytes in the `data` array)
ret, // output buffer
64 // output length
)
if iszero(success) {
revert(0, 0)
}
}
return (ret[0], ret[1]);
}
bytes4 private constant IS_V28_OR_HIGHER_FUNC_SELECTOR = bytes4(keccak256("isV28OrHigher()"));
// using assembly code to identify converter version
// can't rely on the version number since the function had a different signature in older converters
function isV28OrHigherConverter(IConverter _converter) internal view returns (bool) {
bool success;
uint256[1] memory ret;
bytes memory data = abi.encodeWithSelector(IS_V28_OR_HIGHER_FUNC_SELECTOR);
assembly {
success := staticcall(
5000, // isV28OrHigher consumes 190 gas, but just for extra safety
_converter, // destination address
add(data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
mload(data), // input length (loaded from the first 32 bytes in the `data` array)
ret, // output buffer
32 // output length
)
}
return success && ret[0] != 0;
}
/**
* @dev deprecated, backward compatibility
*/
function getReturnByPath(IERC20Token[] _path, uint256 _amount) public view returns (uint256, uint256) {
return (rateByPath(_path, _amount), 0);
}
/**
* @dev deprecated, backward compatibility
*/
function convert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public payable returns (uint256) {
return convertByPath(_path, _amount, _minReturn, address(0), address(0), 0);
}
/**
* @dev deprecated, backward compatibility
*/
function convert2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _affiliateAccount,
uint256 _affiliateFee
)
public
payable
returns (uint256)
{
return convertByPath(_path, _amount, _minReturn, address(0), _affiliateAccount, _affiliateFee);
}
/**
* @dev deprecated, backward compatibility
*/
function convertFor(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _beneficiary) public payable returns (uint256) {
return convertByPath(_path, _amount, _minReturn, _beneficiary, address(0), 0);
}
/**
* @dev deprecated, backward compatibility
*/
function convertFor2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _beneficiary,
address _affiliateAccount,
uint256 _affiliateFee
)
public
payable
greaterThanZero(_minReturn)
returns (uint256)
{
return convertByPath(_path, _amount, _minReturn, _beneficiary, _affiliateAccount, _affiliateFee);
}
/**
* @dev deprecated, backward compatibility
*/
function claimAndConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public returns (uint256) {
return convertByPath(_path, _amount, _minReturn, address(0), address(0), 0);
}
/**
* @dev deprecated, backward compatibility
*/
function claimAndConvert2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _affiliateAccount,
uint256 _affiliateFee
)
public
returns (uint256)
{
return convertByPath(_path, _amount, _minReturn, address(0), _affiliateAccount, _affiliateFee);
}
/**
* @dev deprecated, backward compatibility
*/
function claimAndConvertFor(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _beneficiary) public returns (uint256) {
return convertByPath(_path, _amount, _minReturn, _beneficiary, address(0), 0);
}
/**
* @dev deprecated, backward compatibility
*/
function claimAndConvertFor2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _beneficiary,
address _affiliateAccount,
uint256 _affiliateFee
)
public
returns (uint256)
{
return convertByPath(_path, _amount, _minReturn, _beneficiary, _affiliateAccount, _affiliateFee);
}
}File 2 of 8: AElfToken
pragma solidity ^0.4.18;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 {
uint256 public totalSupply;
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract AElfToken is ERC20, Ownable {
using SafeMath for uint256;
// the controller of minting and destroying tokens
address public aelfDevMultisig = 0x6d3E0B5abFc141cAa674a3c11e1580e6fff2a0B9;
// the controller of approving of minting and withdraw tokens
address public aelfCommunityMultisig = 0x4885B422656D4B316C9C7Abc0c0Ab31A2677d9f0;
struct TokensWithLock {
uint256 value;
uint256 blockNumber;
}
// Balances for each account
mapping(address => uint256) balances;
// Tokens with time lock
// Only when the tokens' blockNumber is less than current block number,
// can the tokens be minted to the owner
mapping(address => TokensWithLock) lockTokens;
// Owner of account approves the transfer of an amount to another account
mapping(address => mapping (address => uint256)) allowed;
// Token Cap
uint256 public totalSupplyCap = 1e27;
// Token Info
string public name = "ELF Token";
string public symbol = "ELF";
uint8 public decimals = 18;
bool public mintingFinished = false;
// the block number when deploy
uint256 public deployBlockNumber = getCurrentBlockNumber();
// the min threshold of lock time
uint256 public constant TIMETHRESHOLD = 7200;
// the time when mintTokensWithinTime can be called
uint256 public constant MINTTIME = 216000;
// the lock time of minted tokens
uint256 public durationOfLock = 7200;
// True if transfers are allowed
bool public transferable = false;
// True if the transferable can be change
bool public canSetTransferable = true;
modifier canMint() {
require(!mintingFinished);
_;
}
modifier only(address _address) {
require(msg.sender == _address);
_;
}
modifier nonZeroAddress(address _address) {
require(_address != address(0));
_;
}
modifier canTransfer() {
require(transferable == true);
_;
}
event SetDurationOfLock(address indexed _caller);
event ApproveMintTokens(address indexed _owner, uint256 _amount);
event WithdrawMintTokens(address indexed _owner, uint256 _amount);
event MintTokens(address indexed _owner, uint256 _amount);
event BurnTokens(address indexed _owner, uint256 _amount);
event MintFinished(address indexed _caller);
event SetTransferable(address indexed _address, bool _transferable);
event SetAElfDevMultisig(address indexed _old, address indexed _new);
event SetAElfCommunityMultisig(address indexed _old, address indexed _new);
event DisableSetTransferable(address indexed _address, bool _canSetTransferable);
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) canTransfer public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) canTransfer public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) canTransfer public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
/**
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
*/
function increaseApproval(address _spender, uint256 _addedValue) canTransfer public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
function decreaseApproval(address _spender, uint256 _subtractedValue) canTransfer public returns (bool) {
uint256 oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Enables token holders to transfer their tokens freely if true
* @param _transferable True if transfers are allowed
*/
function setTransferable(bool _transferable) only(aelfDevMultisig) public {
require(canSetTransferable == true);
transferable = _transferable;
SetTransferable(msg.sender, _transferable);
}
/**
* @dev disable the canSetTransferable
*/
function disableSetTransferable() only(aelfDevMultisig) public {
transferable = true;
canSetTransferable = false;
DisableSetTransferable(msg.sender, false);
}
/**
* @dev Set the aelfMultisig
* @param _aelfDevMultisig The new aelfMultisig
*/
function setAElfDevMultisig(address _aelfDevMultisig) only(aelfDevMultisig) nonZeroAddress(_aelfDevMultisig) public {
aelfDevMultisig = _aelfDevMultisig;
SetAElfDevMultisig(msg.sender, _aelfDevMultisig);
}
/**
* @dev Set the aelfCommunityMultisig
* @param _aelfCommunityMultisig The new aelfCommunityMultisig
*/
function setAElfCommunityMultisig(address _aelfCommunityMultisig) only(aelfCommunityMultisig) nonZeroAddress(_aelfCommunityMultisig) public {
aelfCommunityMultisig = _aelfCommunityMultisig;
SetAElfCommunityMultisig(msg.sender, _aelfCommunityMultisig);
}
/**
* @dev Set the duration of lock of tokens approved of minting
* @param _durationOfLock the new duration of lock
*/
function setDurationOfLock(uint256 _durationOfLock) canMint only(aelfCommunityMultisig) public {
require(_durationOfLock >= TIMETHRESHOLD);
durationOfLock = _durationOfLock;
SetDurationOfLock(msg.sender);
}
/**
* @dev Get the quantity of locked tokens
* @param _owner The address of locked tokens
* @return the quantity and the lock time of locked tokens
*/
function getLockTokens(address _owner) nonZeroAddress(_owner) view public returns (uint256 value, uint256 blockNumber) {
return (lockTokens[_owner].value, lockTokens[_owner].blockNumber);
}
/**
* @dev Approve of minting `_amount` tokens that are assigned to `_owner`
* @param _owner The address that will be assigned the new tokens
* @param _amount The quantity of tokens approved of mintting
* @return True if the tokens are approved of mintting correctly
*/
function approveMintTokens(address _owner, uint256 _amount) nonZeroAddress(_owner) canMint only(aelfCommunityMultisig) public returns (bool) {
require(_amount > 0);
uint256 previousLockTokens = lockTokens[_owner].value;
require(previousLockTokens + _amount >= previousLockTokens);
uint256 curTotalSupply = totalSupply;
require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
require(curTotalSupply + _amount <= totalSupplyCap); // Check for overflow of total supply cap
uint256 previousBalanceTo = balanceOf(_owner);
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
lockTokens[_owner].value = previousLockTokens.add(_amount);
uint256 curBlockNumber = getCurrentBlockNumber();
lockTokens[_owner].blockNumber = curBlockNumber.add(durationOfLock);
ApproveMintTokens(_owner, _amount);
return true;
}
/**
* @dev Withdraw approval of minting `_amount` tokens that are assigned to `_owner`
* @param _owner The address that will be withdrawn the tokens
* @param _amount The quantity of tokens withdrawn approval of mintting
* @return True if the tokens are withdrawn correctly
*/
function withdrawMintTokens(address _owner, uint256 _amount) nonZeroAddress(_owner) canMint only(aelfCommunityMultisig) public returns (bool) {
require(_amount > 0);
uint256 previousLockTokens = lockTokens[_owner].value;
require(previousLockTokens - _amount >= 0);
lockTokens[_owner].value = previousLockTokens.sub(_amount);
if (previousLockTokens - _amount == 0) {
lockTokens[_owner].blockNumber = 0;
}
WithdrawMintTokens(_owner, _amount);
return true;
}
/**
* @dev Mints `_amount` tokens that are assigned to `_owner`
* @param _owner The address that will be assigned the new tokens
* @return True if the tokens are minted correctly
*/
function mintTokens(address _owner) canMint only(aelfDevMultisig) nonZeroAddress(_owner) public returns (bool) {
require(lockTokens[_owner].blockNumber <= getCurrentBlockNumber());
uint256 _amount = lockTokens[_owner].value;
uint256 curTotalSupply = totalSupply;
require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
require(curTotalSupply + _amount <= totalSupplyCap); // Check for overflow of total supply cap
uint256 previousBalanceTo = balanceOf(_owner);
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
totalSupply = curTotalSupply.add(_amount);
balances[_owner] = previousBalanceTo.add(_amount);
lockTokens[_owner].value = 0;
lockTokens[_owner].blockNumber = 0;
MintTokens(_owner, _amount);
Transfer(0, _owner, _amount);
return true;
}
/**
* @dev Mints `_amount` tokens that are assigned to `_owner` within one day after deployment
* the tokens minted will be added to balance immediately
* @param _owner The address that will be assigned the new tokens
* @param _amount The quantity of tokens withdrawn minted
* @return True if the tokens are minted correctly
*/
function mintTokensWithinTime(address _owner, uint256 _amount) nonZeroAddress(_owner) canMint only(aelfDevMultisig) public returns (bool) {
require(_amount > 0);
require(getCurrentBlockNumber() < (deployBlockNumber + MINTTIME));
uint256 curTotalSupply = totalSupply;
require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
require(curTotalSupply + _amount <= totalSupplyCap); // Check for overflow of total supply cap
uint256 previousBalanceTo = balanceOf(_owner);
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
totalSupply = curTotalSupply.add(_amount);
balances[_owner] = previousBalanceTo.add(_amount);
MintTokens(_owner, _amount);
Transfer(0, _owner, _amount);
return true;
}
/**
* @dev Transfer tokens to multiple addresses
* @param _addresses The addresses that will receieve tokens
* @param _amounts The quantity of tokens that will be transferred
* @return True if the tokens are transferred correctly
*/
function transferForMultiAddresses(address[] _addresses, uint256[] _amounts) canTransfer public returns (bool) {
for (uint256 i = 0; i < _addresses.length; i++) {
require(_addresses[i] != address(0));
require(_amounts[i] <= balances[msg.sender]);
require(_amounts[i] > 0);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_amounts[i]);
balances[_addresses[i]] = balances[_addresses[i]].add(_amounts[i]);
Transfer(msg.sender, _addresses[i], _amounts[i]);
}
return true;
}
/**
* @dev Burns `_amount` tokens from `_owner`
* @param _amount The quantity of tokens being burned
* @return True if the tokens are burned correctly
*/
function burnTokens(uint256 _amount) public returns (bool) {
require(_amount > 0);
uint256 curTotalSupply = totalSupply;
require(curTotalSupply >= _amount);
uint256 previousBalanceTo = balanceOf(msg.sender);
require(previousBalanceTo >= _amount);
totalSupply = curTotalSupply.sub(_amount);
balances[msg.sender] = previousBalanceTo.sub(_amount);
BurnTokens(msg.sender, _amount);
Transfer(msg.sender, 0, _amount);
return true;
}
/**
* @dev Function to stop minting new tokens.
* @return True if the operation was successful.
*/
function finishMinting() only(aelfDevMultisig) canMint public returns (bool) {
mintingFinished = true;
MintFinished(msg.sender);
return true;
}
function getCurrentBlockNumber() private view returns (uint256) {
return block.number;
}
}File 3 of 8: LiquidityPoolV1Converter
// File: contracts/utility/interfaces/IOwned.sol
pragma solidity 0.4.26;
/*
Owned contract interface
*/
contract IOwned {
// this function isn't abstract since the compiler emits automatically generated getter functions as external
function owner() public view returns (address) {this;}
function transferOwnership(address _newOwner) public;
function acceptOwnership() public;
}
// File: contracts/token/interfaces/IERC20Token.sol
pragma solidity 0.4.26;
/*
ERC20 Standard Token interface
*/
contract IERC20Token {
// these functions aren't abstract since the compiler emits automatically generated getter functions as external
function name() public view returns (string) {this;}
function symbol() public view returns (string) {this;}
function decimals() public view returns (uint8) {this;}
function totalSupply() public view returns (uint256) {this;}
function balanceOf(address _owner) public view returns (uint256) {_owner; this;}
function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;}
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
}
// File: contracts/utility/interfaces/ITokenHolder.sol
pragma solidity 0.4.26;
/*
Token Holder interface
*/
contract ITokenHolder is IOwned {
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
}
// File: contracts/converter/interfaces/IConverterAnchor.sol
pragma solidity 0.4.26;
/*
Converter Anchor interface
*/
contract IConverterAnchor is IOwned, ITokenHolder {
}
// File: contracts/utility/interfaces/IWhitelist.sol
pragma solidity 0.4.26;
/*
Whitelist interface
*/
contract IWhitelist {
function isWhitelisted(address _address) public view returns (bool);
}
// File: contracts/converter/interfaces/IConverter.sol
pragma solidity 0.4.26;
/*
Converter interface
*/
contract IConverter is IOwned {
function converterType() public pure returns (uint16);
function anchor() public view returns (IConverterAnchor) {this;}
function isActive() public view returns (bool);
function targetAmountAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view returns (uint256, uint256);
function convert(IERC20Token _sourceToken,
IERC20Token _targetToken,
uint256 _amount,
address _trader,
address _beneficiary) public payable returns (uint256);
function conversionWhitelist() public view returns (IWhitelist) {this;}
function conversionFee() public view returns (uint32) {this;}
function maxConversionFee() public view returns (uint32) {this;}
function reserveBalance(IERC20Token _reserveToken) public view returns (uint256);
function() external payable;
function transferAnchorOwnership(address _newOwner) public;
function acceptAnchorOwnership() public;
function setConversionFee(uint32 _conversionFee) public;
function setConversionWhitelist(IWhitelist _whitelist) public;
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
function withdrawETH(address _to) public;
function addReserve(IERC20Token _token, uint32 _ratio) public;
// deprecated, backward compatibility
function token() public view returns (IConverterAnchor);
function transferTokenOwnership(address _newOwner) public;
function acceptTokenOwnership() public;
function connectors(address _address) public view returns (uint256, uint32, bool, bool, bool);
function getConnectorBalance(IERC20Token _connectorToken) public view returns (uint256);
function connectorTokens(uint256 _index) public view returns (IERC20Token);
function connectorTokenCount() public view returns (uint16);
}
// File: contracts/converter/interfaces/IConverterUpgrader.sol
pragma solidity 0.4.26;
/*
Converter Upgrader interface
*/
contract IConverterUpgrader {
function upgrade(bytes32 _version) public;
function upgrade(uint16 _version) public;
}
// File: contracts/converter/interfaces/IBancorFormula.sol
pragma solidity 0.4.26;
/*
Bancor Formula interface
*/
contract IBancorFormula {
function purchaseTargetAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256);
function saleTargetAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256);
function crossReserveTargetAmount(uint256 _sourceReserveBalance,
uint32 _sourceReserveWeight,
uint256 _targetReserveBalance,
uint32 _targetReserveWeight,
uint256 _amount)
public view returns (uint256);
function fundCost(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256);
function fundSupplyAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256);
function liquidateReserveAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256);
}
// File: contracts/IBancorNetwork.sol
pragma solidity 0.4.26;
/*
Bancor Network interface
*/
contract IBancorNetwork {
function convert2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _affiliateAccount,
uint256 _affiliateFee
) public payable returns (uint256);
function claimAndConvert2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _affiliateAccount,
uint256 _affiliateFee
) public returns (uint256);
function convertFor2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _for,
address _affiliateAccount,
uint256 _affiliateFee
) public payable returns (uint256);
function claimAndConvertFor2(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _for,
address _affiliateAccount,
uint256 _affiliateFee
) public returns (uint256);
// deprecated, backward compatibility
function convert(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn
) public payable returns (uint256);
// deprecated, backward compatibility
function claimAndConvert(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn
) public returns (uint256);
// deprecated, backward compatibility
function convertFor(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _for
) public payable returns (uint256);
// deprecated, backward compatibility
function claimAndConvertFor(
IERC20Token[] _path,
uint256 _amount,
uint256 _minReturn,
address _for
) public returns (uint256);
}
// File: contracts/utility/Owned.sol
pragma solidity 0.4.26;
/**
* @dev Provides support and utilities for contract ownership
*/
contract Owned is IOwned {
address public owner;
address public newOwner;
/**
* @dev triggered when the owner is updated
*
* @param _prevOwner previous owner
* @param _newOwner new owner
*/
event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
/**
* @dev initializes a new Owned instance
*/
constructor() public {
owner = msg.sender;
}
// allows execution by the owner only
modifier ownerOnly {
_ownerOnly();
_;
}
// error message binary size optimization
function _ownerOnly() internal view {
require(msg.sender == owner, "ERR_ACCESS_DENIED");
}
/**
* @dev allows transferring the contract ownership
* the new owner still needs to accept the transfer
* can only be called by the contract owner
*
* @param _newOwner new contract owner
*/
function transferOwnership(address _newOwner) public ownerOnly {
require(_newOwner != owner, "ERR_SAME_OWNER");
newOwner = _newOwner;
}
/**
* @dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() public {
require(msg.sender == newOwner, "ERR_ACCESS_DENIED");
emit OwnerUpdate(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
// File: contracts/utility/Utils.sol
pragma solidity 0.4.26;
/**
* @dev Utilities & Common Modifiers
*/
contract Utils {
// verifies that a value is greater than zero
modifier greaterThanZero(uint256 _value) {
_greaterThanZero(_value);
_;
}
// error message binary size optimization
function _greaterThanZero(uint256 _value) internal pure {
require(_value > 0, "ERR_ZERO_VALUE");
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address _address) {
_validAddress(_address);
_;
}
// error message binary size optimization
function _validAddress(address _address) internal pure {
require(_address != address(0), "ERR_INVALID_ADDRESS");
}
// verifies that the address is different than this contract address
modifier notThis(address _address) {
_notThis(_address);
_;
}
// error message binary size optimization
function _notThis(address _address) internal view {
require(_address != address(this), "ERR_ADDRESS_IS_SELF");
}
}
// File: contracts/utility/interfaces/IContractRegistry.sol
pragma solidity 0.4.26;
/*
Contract Registry interface
*/
contract IContractRegistry {
function addressOf(bytes32 _contractName) public view returns (address);
// deprecated, backward compatibility
function getAddress(bytes32 _contractName) public view returns (address);
}
// File: contracts/utility/ContractRegistryClient.sol
pragma solidity 0.4.26;
/**
* @dev Base contract for ContractRegistry clients
*/
contract ContractRegistryClient is Owned, Utils {
bytes32 internal constant CONTRACT_REGISTRY = "ContractRegistry";
bytes32 internal constant BANCOR_NETWORK = "BancorNetwork";
bytes32 internal constant BANCOR_FORMULA = "BancorFormula";
bytes32 internal constant CONVERTER_FACTORY = "ConverterFactory";
bytes32 internal constant CONVERSION_PATH_FINDER = "ConversionPathFinder";
bytes32 internal constant CONVERTER_UPGRADER = "BancorConverterUpgrader";
bytes32 internal constant CONVERTER_REGISTRY = "BancorConverterRegistry";
bytes32 internal constant CONVERTER_REGISTRY_DATA = "BancorConverterRegistryData";
bytes32 internal constant BNT_TOKEN = "BNTToken";
bytes32 internal constant BANCOR_X = "BancorX";
bytes32 internal constant BANCOR_X_UPGRADER = "BancorXUpgrader";
IContractRegistry public registry; // address of the current contract-registry
IContractRegistry public prevRegistry; // address of the previous contract-registry
bool public onlyOwnerCanUpdateRegistry; // only an owner can update the contract-registry
/**
* @dev verifies that the caller is mapped to the given contract name
*
* @param _contractName contract name
*/
modifier only(bytes32 _contractName) {
_only(_contractName);
_;
}
// error message binary size optimization
function _only(bytes32 _contractName) internal view {
require(msg.sender == addressOf(_contractName), "ERR_ACCESS_DENIED");
}
/**
* @dev initializes a new ContractRegistryClient instance
*
* @param _registry address of a contract-registry contract
*/
constructor(IContractRegistry _registry) internal validAddress(_registry) {
registry = IContractRegistry(_registry);
prevRegistry = IContractRegistry(_registry);
}
/**
* @dev updates to the new contract-registry
*/
function updateRegistry() public {
// verify that this function is permitted
require(msg.sender == owner || !onlyOwnerCanUpdateRegistry, "ERR_ACCESS_DENIED");
// get the new contract-registry
IContractRegistry newRegistry = IContractRegistry(addressOf(CONTRACT_REGISTRY));
// verify that the new contract-registry is different and not zero
require(newRegistry != address(registry) && newRegistry != address(0), "ERR_INVALID_REGISTRY");
// verify that the new contract-registry is pointing to a non-zero contract-registry
require(newRegistry.addressOf(CONTRACT_REGISTRY) != address(0), "ERR_INVALID_REGISTRY");
// save a backup of the current contract-registry before replacing it
prevRegistry = registry;
// replace the current contract-registry with the new contract-registry
registry = newRegistry;
}
/**
* @dev restores the previous contract-registry
*/
function restoreRegistry() public ownerOnly {
// restore the previous contract-registry
registry = prevRegistry;
}
/**
* @dev restricts the permission to update the contract-registry
*
* @param _onlyOwnerCanUpdateRegistry indicates whether or not permission is restricted to owner only
*/
function restrictRegistryUpdate(bool _onlyOwnerCanUpdateRegistry) public ownerOnly {
// change the permission to update the contract-registry
onlyOwnerCanUpdateRegistry = _onlyOwnerCanUpdateRegistry;
}
/**
* @dev returns the address associated with the given contract name
*
* @param _contractName contract name
*
* @return contract address
*/
function addressOf(bytes32 _contractName) internal view returns (address) {
return registry.addressOf(_contractName);
}
}
// File: contracts/utility/ReentrancyGuard.sol
pragma solidity 0.4.26;
/**
* @dev ReentrancyGuard
*
* The contract provides protection against re-entrancy - calling a function (directly or
* indirectly) from within itself.
*/
contract ReentrancyGuard {
// true while protected code is being executed, false otherwise
bool private locked = false;
/**
* @dev ensures instantiation only by sub-contracts
*/
constructor() internal {}
// protects a function against reentrancy attacks
modifier protected() {
_protected();
locked = true;
_;
locked = false;
}
// error message binary size optimization
function _protected() internal view {
require(!locked, "ERR_REENTRANCY");
}
}
// File: contracts/utility/SafeMath.sol
pragma solidity 0.4.26;
/**
* @dev Library for basic math operations with overflow/underflow protection
*/
library SafeMath {
/**
* @dev returns the sum of _x and _y, reverts if the calculation overflows
*
* @param _x value 1
* @param _y value 2
*
* @return sum
*/
function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
uint256 z = _x + _y;
require(z >= _x, "ERR_OVERFLOW");
return z;
}
/**
* @dev returns the difference of _x minus _y, reverts if the calculation underflows
*
* @param _x minuend
* @param _y subtrahend
*
* @return difference
*/
function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_x >= _y, "ERR_UNDERFLOW");
return _x - _y;
}
/**
* @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
*
* @param _x factor 1
* @param _y factor 2
*
* @return product
*/
function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
// gas optimization
if (_x == 0)
return 0;
uint256 z = _x * _y;
require(z / _x == _y, "ERR_OVERFLOW");
return z;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*
* @param _x dividend
* @param _y divisor
*
* @return quotient
*/
function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_y > 0, "ERR_DIVIDE_BY_ZERO");
uint256 c = _x / _y;
return c;
}
}
// File: contracts/utility/TokenHandler.sol
pragma solidity 0.4.26;
contract TokenHandler {
bytes4 private constant APPROVE_FUNC_SELECTOR = bytes4(keccak256("approve(address,uint256)"));
bytes4 private constant TRANSFER_FUNC_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
bytes4 private constant TRANSFER_FROM_FUNC_SELECTOR = bytes4(keccak256("transferFrom(address,address,uint256)"));
/**
* @dev executes the ERC20 token's `approve` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _spender approved address
* @param _value allowance amount
*/
function safeApprove(IERC20Token _token, address _spender, uint256 _value) internal {
execute(_token, abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value));
}
/**
* @dev executes the ERC20 token's `transfer` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _to target address
* @param _value transfer amount
*/
function safeTransfer(IERC20Token _token, address _to, uint256 _value) internal {
execute(_token, abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value));
}
/**
* @dev executes the ERC20 token's `transferFrom` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _from source address
* @param _to target address
* @param _value transfer amount
*/
function safeTransferFrom(IERC20Token _token, address _from, address _to, uint256 _value) internal {
execute(_token, abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value));
}
/**
* @dev executes a function on the ERC20 token and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _data data to pass in to the token's contract for execution
*/
function execute(IERC20Token _token, bytes memory _data) private {
uint256[1] memory ret = [uint256(1)];
assembly {
let success := call(
gas, // gas remaining
_token, // destination address
0, // no ether
add(_data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
mload(_data), // input length (loaded from the first 32 bytes in the `data` array)
ret, // output buffer
32 // output length
)
if iszero(success) {
revert(0, 0)
}
}
require(ret[0] != 0, "ERR_TRANSFER_FAILED");
}
}
// File: contracts/utility/TokenHolder.sol
pragma solidity 0.4.26;
/**
* @dev We consider every contract to be a 'token holder' since it's currently not possible
* for a contract to deny receiving tokens.
*
* The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
* the owner to send tokens that were sent to the contract by mistake back to their sender.
*
* Note that we use the non standard ERC-20 interface which has no return value for transfer
* in order to support both non standard as well as standard token contracts.
* see https://github.com/ethereum/solidity/issues/4116
*/
contract TokenHolder is ITokenHolder, TokenHandler, Owned, Utils {
/**
* @dev withdraws tokens held by the contract and sends them to an account
* can only be called by the owner
*
* @param _token ERC20 token contract address
* @param _to account to receive the new amount
* @param _amount amount to withdraw
*/
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
public
ownerOnly
validAddress(_token)
validAddress(_to)
notThis(_to)
{
safeTransfer(_token, _to, _amount);
}
}
// File: contracts/token/interfaces/IEtherToken.sol
pragma solidity 0.4.26;
/*
Ether Token interface
*/
contract IEtherToken is IERC20Token {
function deposit() public payable;
function withdraw(uint256 _amount) public;
function depositTo(address _to) public payable;
function withdrawTo(address _to, uint256 _amount) public;
}
// File: contracts/bancorx/interfaces/IBancorX.sol
pragma solidity 0.4.26;
contract IBancorX {
function token() public view returns (IERC20Token) {this;}
function xTransfer(bytes32 _toBlockchain, bytes32 _to, uint256 _amount, uint256 _id) public;
function getXTransferAmount(uint256 _xTransferId, address _for) public view returns (uint256);
}
// File: contracts/converter/ConverterBase.sol
pragma solidity 0.4.26;
/**
* @dev ConverterBase
*
* The converter contains the main logic for conversions between different ERC20 tokens.
*
* It is also the upgradable part of the mechanism (note that upgrades are opt-in).
*
* The anchor must be set on construction and cannot be changed afterwards.
* Wrappers are provided for some of the anchor's functions, for easier access.
*
* Once the converter accepts ownership of the anchor, it becomes the anchor's sole controller
* and can execute any of its functions.
*
* To upgrade the converter, anchor ownership must be transferred to a new converter, along with
* any relevant data.
*
* Note that the converter can transfer anchor ownership to a new converter that
* doesn't allow upgrades anymore, for finalizing the relationship between the converter
* and the anchor.
*
* Converter types (defined as uint16 type) -
* 0 = liquid token converter
* 1 = liquidity pool v1 converter
* 2 = liquidity pool v2 converter
*
* Note that converters don't currently support tokens with transfer fees.
*/
contract ConverterBase is IConverter, TokenHandler, TokenHolder, ContractRegistryClient, ReentrancyGuard {
using SafeMath for uint256;
uint32 internal constant WEIGHT_RESOLUTION = 1000000;
uint64 internal constant CONVERSION_FEE_RESOLUTION = 1000000;
address internal constant ETH_RESERVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
struct Reserve {
uint256 balance; // reserve balance
uint32 weight; // reserve weight, represented in ppm, 1-1000000
bool deprecated1; // deprecated
bool deprecated2; // deprecated
bool isSet; // true if the reserve is valid, false otherwise
}
/**
* @dev version number
*/
uint16 public constant version = 30;
IConverterAnchor public anchor; // converter anchor contract
IWhitelist public conversionWhitelist; // whitelist contract with list of addresses that are allowed to use the converter
IERC20Token[] public reserveTokens; // ERC20 standard token addresses (prior version 17, use 'connectorTokens' instead)
mapping (address => Reserve) public reserves; // reserve token addresses -> reserve data (prior version 17, use 'connectors' instead)
uint32 public reserveRatio = 0; // ratio between the reserves and the market cap, equal to the total reserve weights
uint32 public maxConversionFee = 0; // maximum conversion fee for the lifetime of the contract,
// represented in ppm, 0...1000000 (0 = no fee, 100 = 0.01%, 1000000 = 100%)
uint32 public conversionFee = 0; // current conversion fee, represented in ppm, 0...maxConversionFee
bool public constant conversionsEnabled = true; // deprecated, backward compatibility
/**
* @dev triggered when the converter is activated
*
* @param _anchor converter anchor
* @param _activated true if the converter was activated, false if it was deactivated
*/
event Activation(IConverterAnchor _anchor, bool _activated);
/**
* @dev triggered when a conversion between two tokens occurs
*
* @param _fromToken source ERC20 token
* @param _toToken target ERC20 token
* @param _trader wallet that initiated the trade
* @param _amount amount converted, in the source token
* @param _return amount returned, minus conversion fee
* @param _conversionFee conversion fee
*/
event Conversion(
address indexed _fromToken,
address indexed _toToken,
address indexed _trader,
uint256 _amount,
uint256 _return,
int256 _conversionFee
);
/**
* @dev triggered when the rate between two tokens in the converter changes
* note that the event might be dispatched for rate updates between any two tokens in the converter
* note that prior to version 28, you should use the 'PriceDataUpdate' event instead
*
* @param _token1 address of the first token
* @param _token2 address of the second token
* @param _rateN rate of 1 unit of `_token1` in `_token2` (numerator)
* @param _rateD rate of 1 unit of `_token1` in `_token2` (denominator)
*/
event TokenRateUpdate(
address indexed _token1,
address indexed _token2,
uint256 _rateN,
uint256 _rateD
);
/**
* @dev triggered when the conversion fee is updated
*
* @param _prevFee previous fee percentage, represented in ppm
* @param _newFee new fee percentage, represented in ppm
*/
event ConversionFeeUpdate(uint32 _prevFee, uint32 _newFee);
/**
* @dev used by sub-contracts to initialize a new converter
*
* @param _anchor anchor governed by the converter
* @param _registry address of a contract registry contract
* @param _maxConversionFee maximum conversion fee, represented in ppm
*/
constructor(
IConverterAnchor _anchor,
IContractRegistry _registry,
uint32 _maxConversionFee
)
validAddress(_anchor)
ContractRegistryClient(_registry)
internal
validConversionFee(_maxConversionFee)
{
anchor = _anchor;
maxConversionFee = _maxConversionFee;
}
// ensures that the converter is active
modifier active() {
_active();
_;
}
// error message binary size optimization
function _active() internal view {
require(isActive(), "ERR_INACTIVE");
}
// ensures that the converter is not active
modifier inactive() {
_inactive();
_;
}
// error message binary size optimization
function _inactive() internal view {
require(!isActive(), "ERR_ACTIVE");
}
// validates a reserve token address - verifies that the address belongs to one of the reserve tokens
modifier validReserve(IERC20Token _address) {
_validReserve(_address);
_;
}
// error message binary size optimization
function _validReserve(IERC20Token _address) internal view {
require(reserves[_address].isSet, "ERR_INVALID_RESERVE");
}
// validates conversion fee
modifier validConversionFee(uint32 _conversionFee) {
_validConversionFee(_conversionFee);
_;
}
// error message binary size optimization
function _validConversionFee(uint32 _conversionFee) internal pure {
require(_conversionFee <= CONVERSION_FEE_RESOLUTION, "ERR_INVALID_CONVERSION_FEE");
}
// validates reserve weight
modifier validReserveWeight(uint32 _weight) {
_validReserveWeight(_weight);
_;
}
// error message binary size optimization
function _validReserveWeight(uint32 _weight) internal pure {
require(_weight > 0 && _weight <= WEIGHT_RESOLUTION, "ERR_INVALID_RESERVE_WEIGHT");
}
/**
* @dev deposits ether
* can only be called if the converter has an ETH reserve
*/
function() external payable {
require(reserves[ETH_RESERVE_ADDRESS].isSet, "ERR_INVALID_RESERVE"); // require(hasETHReserve(), "ERR_INVALID_RESERVE");
// a workaround for a problem when running solidity-coverage
// see https://github.com/sc-forks/solidity-coverage/issues/487
}
/**
* @dev withdraws ether
* can only be called by the owner if the converter is inactive or by upgrader contract
* can only be called after the upgrader contract has accepted the ownership of this contract
* can only be called if the converter has an ETH reserve
*
* @param _to address to send the ETH to
*/
function withdrawETH(address _to)
public
protected
ownerOnly
validReserve(IERC20Token(ETH_RESERVE_ADDRESS))
{
address converterUpgrader = addressOf(CONVERTER_UPGRADER);
// verify that the converter is inactive or that the owner is the upgrader contract
require(!isActive() || owner == converterUpgrader, "ERR_ACCESS_DENIED");
_to.transfer(address(this).balance);
// sync the ETH reserve balance
syncReserveBalance(IERC20Token(ETH_RESERVE_ADDRESS));
}
/**
* @dev checks whether or not the converter version is 28 or higher
*
* @return true, since the converter version is 28 or higher
*/
function isV28OrHigher() public pure returns (bool) {
return true;
}
/**
* @dev allows the owner to update & enable the conversion whitelist contract address
* when set, only addresses that are whitelisted are actually allowed to use the converter
* note that the whitelist check is actually done by the BancorNetwork contract
*
* @param _whitelist address of a whitelist contract
*/
function setConversionWhitelist(IWhitelist _whitelist)
public
ownerOnly
notThis(_whitelist)
{
conversionWhitelist = _whitelist;
}
/**
* @dev returns true if the converter is active, false otherwise
*/
function isActive() public view returns (bool) {
return anchor.owner() == address(this);
}
/**
* @dev transfers the anchor ownership
* the new owner needs to accept the transfer
* can only be called by the converter upgrder while the upgrader is the owner
* note that prior to version 28, you should use 'transferAnchorOwnership' instead
*
* @param _newOwner new token owner
*/
function transferAnchorOwnership(address _newOwner)
public
ownerOnly
only(CONVERTER_UPGRADER)
{
anchor.transferOwnership(_newOwner);
}
/**
* @dev accepts ownership of the anchor after an ownership transfer
* most converters are also activated as soon as they accept the anchor ownership
* can only be called by the contract owner
* note that prior to version 28, you should use 'acceptTokenOwnership' instead
*/
function acceptAnchorOwnership() public ownerOnly {
// verify the the converter has at least one reserve
require(reserveTokenCount() > 0, "ERR_INVALID_RESERVE_COUNT");
anchor.acceptOwnership();
syncReserveBalances();
}
/**
* @dev withdraws tokens held by the anchor and sends them to an account
* can only be called by the owner
*
* @param _token ERC20 token contract address
* @param _to account to receive the new amount
* @param _amount amount to withdraw
*/
function withdrawFromAnchor(IERC20Token _token, address _to, uint256 _amount) public ownerOnly {
anchor.withdrawTokens(_token, _to, _amount);
}
/**
* @dev updates the current conversion fee
* can only be called by the contract owner
*
* @param _conversionFee new conversion fee, represented in ppm
*/
function setConversionFee(uint32 _conversionFee) public ownerOnly {
require(_conversionFee <= maxConversionFee, "ERR_INVALID_CONVERSION_FEE");
emit ConversionFeeUpdate(conversionFee, _conversionFee);
conversionFee = _conversionFee;
}
/**
* @dev withdraws tokens held by the converter and sends them to an account
* can only be called by the owner
* note that reserve tokens can only be withdrawn by the owner while the converter is inactive
* unless the owner is the converter upgrader contract
*
* @param _token ERC20 token contract address
* @param _to account to receive the new amount
* @param _amount amount to withdraw
*/
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public protected ownerOnly {
address converterUpgrader = addressOf(CONVERTER_UPGRADER);
// if the token is not a reserve token, allow withdrawal
// otherwise verify that the converter is inactive or that the owner is the upgrader contract
require(!reserves[_token].isSet || !isActive() || owner == converterUpgrader, "ERR_ACCESS_DENIED");
super.withdrawTokens(_token, _to, _amount);
// if the token is a reserve token, sync the reserve balance
if (reserves[_token].isSet)
syncReserveBalance(_token);
}
/**
* @dev upgrades the converter to the latest version
* can only be called by the owner
* note that the owner needs to call acceptOwnership on the new converter after the upgrade
*/
function upgrade() public ownerOnly {
IConverterUpgrader converterUpgrader = IConverterUpgrader(addressOf(CONVERTER_UPGRADER));
transferOwnership(converterUpgrader);
converterUpgrader.upgrade(version);
acceptOwnership();
}
/**
* @dev returns the number of reserve tokens defined
* note that prior to version 17, you should use 'connectorTokenCount' instead
*
* @return number of reserve tokens
*/
function reserveTokenCount() public view returns (uint16) {
return uint16(reserveTokens.length);
}
/**
* @dev defines a new reserve token for the converter
* can only be called by the owner while the converter is inactive
*
* @param _token address of the reserve token
* @param _weight reserve weight, represented in ppm, 1-1000000
*/
function addReserve(IERC20Token _token, uint32 _weight)
public
ownerOnly
inactive
validAddress(_token)
notThis(_token)
validReserveWeight(_weight)
{
// validate input
require(_token != address(anchor) && !reserves[_token].isSet, "ERR_INVALID_RESERVE");
require(_weight <= WEIGHT_RESOLUTION - reserveRatio, "ERR_INVALID_RESERVE_WEIGHT");
require(reserveTokenCount() < uint16(-1), "ERR_INVALID_RESERVE_COUNT");
Reserve storage newReserve = reserves[_token];
newReserve.balance = 0;
newReserve.weight = _weight;
newReserve.isSet = true;
reserveTokens.push(_token);
reserveRatio += _weight;
}
/**
* @dev returns the reserve's weight
* added in version 28
*
* @param _reserveToken reserve token contract address
*
* @return reserve weight
*/
function reserveWeight(IERC20Token _reserveToken)
public
view
validReserve(_reserveToken)
returns (uint32)
{
return reserves[_reserveToken].weight;
}
/**
* @dev returns the reserve's balance
* note that prior to version 17, you should use 'getConnectorBalance' instead
*
* @param _reserveToken reserve token contract address
*
* @return reserve balance
*/
function reserveBalance(IERC20Token _reserveToken)
public
view
validReserve(_reserveToken)
returns (uint256)
{
return reserves[_reserveToken].balance;
}
/**
* @dev checks whether or not the converter has an ETH reserve
*
* @return true if the converter has an ETH reserve, false otherwise
*/
function hasETHReserve() public view returns (bool) {
return reserves[ETH_RESERVE_ADDRESS].isSet;
}
/**
* @dev converts a specific amount of source tokens to target tokens
* can only be called by the bancor network contract
*
* @param _sourceToken source ERC20 token
* @param _targetToken target ERC20 token
* @param _amount amount of tokens to convert (in units of the source token)
* @param _trader address of the caller who executed the conversion
* @param _beneficiary wallet to receive the conversion result
*
* @return amount of tokens received (in units of the target token)
*/
function convert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address _beneficiary)
public
payable
protected
only(BANCOR_NETWORK)
returns (uint256)
{
// validate input
require(_sourceToken != _targetToken, "ERR_SAME_SOURCE_TARGET");
// if a whitelist is set, verify that both and trader and the beneficiary are whitelisted
require(conversionWhitelist == address(0) ||
(conversionWhitelist.isWhitelisted(_trader) && conversionWhitelist.isWhitelisted(_beneficiary)),
"ERR_NOT_WHITELISTED");
return doConvert(_sourceToken, _targetToken, _amount, _trader, _beneficiary);
}
/**
* @dev converts a specific amount of source tokens to target tokens
* called by ConverterBase and allows the inherited contracts to implement custom conversion logic
*
* @param _sourceToken source ERC20 token
* @param _targetToken target ERC20 token
* @param _amount amount of tokens to convert (in units of the source token)
* @param _trader address of the caller who executed the conversion
* @param _beneficiary wallet to receive the conversion result
*
* @return amount of tokens received (in units of the target token)
*/
function doConvert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address _beneficiary) internal returns (uint256);
/**
* @dev returns the conversion fee for a given return amount
*
* @param _amount return amount
*
* @return conversion fee
*/
function calculateFee(uint256 _amount) internal view returns (uint256) {
return _amount.mul(conversionFee).div(CONVERSION_FEE_RESOLUTION);
}
/**
* @dev syncs the stored reserve balance for a given reserve with the real reserve balance
*
* @param _reserveToken address of the reserve token
*/
function syncReserveBalance(IERC20Token _reserveToken) internal validReserve(_reserveToken) {
if (_reserveToken == ETH_RESERVE_ADDRESS)
reserves[_reserveToken].balance = address(this).balance;
else
reserves[_reserveToken].balance = _reserveToken.balanceOf(this);
}
/**
* @dev syncs all stored reserve balances
*/
function syncReserveBalances() internal {
uint256 reserveCount = reserveTokens.length;
for (uint256 i = 0; i < reserveCount; i++)
syncReserveBalance(reserveTokens[i]);
}
/**
* @dev helper, dispatches the Conversion event
*
* @param _sourceToken source ERC20 token
* @param _targetToken target ERC20 token
* @param _trader address of the caller who executed the conversion
* @param _amount amount purchased/sold (in the source token)
* @param _returnAmount amount returned (in the target token)
*/
function dispatchConversionEvent(
IERC20Token _sourceToken,
IERC20Token _targetToken,
address _trader,
uint256 _amount,
uint256 _returnAmount,
uint256 _feeAmount)
internal
{
// fee amount is converted to 255 bits -
// negative amount means the fee is taken from the source token, positive amount means its taken from the target token
// currently the fee is always taken from the target token
// since we convert it to a signed number, we first ensure that it's capped at 255 bits to prevent overflow
assert(_feeAmount < 2 ** 255);
emit Conversion(_sourceToken, _targetToken, _trader, _amount, _returnAmount, int256(_feeAmount));
}
/**
* @dev deprecated since version 28, backward compatibility - use only for earlier versions
*/
function token() public view returns (IConverterAnchor) {
return anchor;
}
/**
* @dev deprecated, backward compatibility
*/
function transferTokenOwnership(address _newOwner) public ownerOnly {
transferAnchorOwnership(_newOwner);
}
/**
* @dev deprecated, backward compatibility
*/
function acceptTokenOwnership() public ownerOnly {
acceptAnchorOwnership();
}
/**
* @dev deprecated, backward compatibility
*/
function connectors(address _address) public view returns (uint256, uint32, bool, bool, bool) {
Reserve memory reserve = reserves[_address];
return(reserve.balance, reserve.weight, false, false, reserve.isSet);
}
/**
* @dev deprecated, backward compatibility
*/
function connectorTokens(uint256 _index) public view returns (IERC20Token) {
return ConverterBase.reserveTokens[_index];
}
/**
* @dev deprecated, backward compatibility
*/
function connectorTokenCount() public view returns (uint16) {
return reserveTokenCount();
}
/**
* @dev deprecated, backward compatibility
*/
function getConnectorBalance(IERC20Token _connectorToken) public view returns (uint256) {
return reserveBalance(_connectorToken);
}
/**
* @dev deprecated, backward compatibility
*/
function getReturn(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view returns (uint256, uint256) {
return targetAmountAndFee(_sourceToken, _targetToken, _amount);
}
}
// File: contracts/converter/LiquidityPoolConverter.sol
pragma solidity 0.4.26;
/**
* @dev Liquidity Pool Converter
*
* The liquidity pool converter is the base contract for specific types of converters that
* manage liquidity pools.
*
* Liquidity pools have 2 reserves or more and they allow converting between them.
*
* Note that TokenRateUpdate events are dispatched for pool tokens as well.
* The pool token is the first token in the event in that case.
*/
contract LiquidityPoolConverter is ConverterBase {
/**
* @dev triggered after liquidity is added
*
* @param _provider liquidity provider
* @param _reserveToken reserve token address
* @param _amount reserve token amount
* @param _newBalance reserve token new balance
* @param _newSupply pool token new supply
*/
event LiquidityAdded(
address indexed _provider,
address indexed _reserveToken,
uint256 _amount,
uint256 _newBalance,
uint256 _newSupply
);
/**
* @dev triggered after liquidity is removed
*
* @param _provider liquidity provider
* @param _reserveToken reserve token address
* @param _amount reserve token amount
* @param _newBalance reserve token new balance
* @param _newSupply pool token new supply
*/
event LiquidityRemoved(
address indexed _provider,
address indexed _reserveToken,
uint256 _amount,
uint256 _newBalance,
uint256 _newSupply
);
/**
* @dev initializes a new LiquidityPoolConverter instance
*
* @param _anchor anchor governed by the converter
* @param _registry address of a contract registry contract
* @param _maxConversionFee maximum conversion fee, represented in ppm
*/
constructor(
IConverterAnchor _anchor,
IContractRegistry _registry,
uint32 _maxConversionFee
)
ConverterBase(_anchor, _registry, _maxConversionFee)
internal
{
}
/**
* @dev accepts ownership of the anchor after an ownership transfer
* also activates the converter
* can only be called by the contract owner
* note that prior to version 28, you should use 'acceptTokenOwnership' instead
*/
function acceptTokenOwnership() public {
// verify that the converter has at least 2 reserves
require(reserveTokenCount() > 1, "ERR_INVALID_RESERVE_COUNT");
super.acceptTokenOwnership();
}
}
// File: contracts/converter/interfaces/ITypedConverterFactory.sol
pragma solidity 0.4.26;
/*
Typed Converter Factory interface
*/
contract ITypedConverterFactory {
function converterType() public pure returns (uint16);
function createConverter(IConverterAnchor _anchor, IContractRegistry _registry, uint32 _maxConversionFee) public returns (IConverter);
}
// File: contracts/token/interfaces/ISmartToken.sol
pragma solidity 0.4.26;
/*
Smart Token interface
*/
contract ISmartToken is IConverterAnchor, IERC20Token {
function disableTransfers(bool _disable) public;
function issue(address _to, uint256 _amount) public;
function destroy(address _from, uint256 _amount) public;
}
// File: contracts/converter/LiquidityPoolV1Converter.sol
pragma solidity 0.4.26;
/*
LiquidityPoolV1Converter Factory
*/
contract LiquidityPoolV1ConverterFactory is ITypedConverterFactory {
/**
* @dev returns the converter type the factory is associated with
*
* @return converter type
*/
function converterType() public pure returns (uint16) {
return 1;
}
/**
* @dev creates a new converter with the given arguments and transfers
* the ownership to the caller
*
* @param _anchor anchor governed by the converter
* @param _registry address of a contract registry contract
* @param _maxConversionFee maximum conversion fee, represented in ppm
*
* @return a new converter
*/
function createConverter(IConverterAnchor _anchor, IContractRegistry _registry, uint32 _maxConversionFee) public returns (IConverter) {
ConverterBase converter = new LiquidityPoolV1Converter(ISmartToken(_anchor), _registry, _maxConversionFee);
converter.transferOwnership(msg.sender);
return converter;
}
}
/**
* @dev Liquidity Pool v1 Converter
*
* The liquidity pool v1 converter is a specialized version of a converter that manages
* a classic bancor liquidity pool.
*
* Even though classic pools can have many reserves, the most common configuration of
* the pool has 2 reserves with 50%/50% weights.
*/
contract LiquidityPoolV1Converter is LiquidityPoolConverter {
IEtherToken internal etherToken = IEtherToken(0xc0829421C1d260BD3cB3E0F06cfE2D52db2cE315);
/**
* @dev triggered after a conversion with new price data
* deprecated, use `TokenRateUpdate` from version 28 and up
*
* @param _connectorToken reserve token
* @param _tokenSupply smart token supply
* @param _connectorBalance reserve balance
* @param _connectorWeight reserve weight
*/
event PriceDataUpdate(
address indexed _connectorToken,
uint256 _tokenSupply,
uint256 _connectorBalance,
uint32 _connectorWeight
);
/**
* @dev initializes a new LiquidityPoolV1Converter instance
*
* @param _token pool token governed by the converter
* @param _registry address of a contract registry contract
* @param _maxConversionFee maximum conversion fee, represented in ppm
*/
constructor(
ISmartToken _token,
IContractRegistry _registry,
uint32 _maxConversionFee
)
LiquidityPoolConverter(_token, _registry, _maxConversionFee)
public
{
}
/**
* @dev returns the converter type
*
* @return see the converter types in the the main contract doc
*/
function converterType() public pure returns (uint16) {
return 1;
}
/**
* @dev accepts ownership of the anchor after an ownership transfer
* also activates the converter
* can only be called by the contract owner
* note that prior to version 28, you should use 'acceptTokenOwnership' instead
*/
function acceptAnchorOwnership() public ownerOnly {
super.acceptAnchorOwnership();
emit Activation(anchor, true);
}
/**
* @dev returns the expected target amount of converting one reserve to another along with the fee
*
* @param _sourceToken contract address of the source reserve token
* @param _targetToken contract address of the target reserve token
* @param _amount amount of tokens received from the user
*
* @return expected target amount
* @return expected fee
*/
function targetAmountAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount)
public
view
active
validReserve(_sourceToken)
validReserve(_targetToken)
returns (uint256, uint256)
{
// validate input
require(_sourceToken != _targetToken, "ERR_SAME_SOURCE_TARGET");
uint256 amount = IBancorFormula(addressOf(BANCOR_FORMULA)).crossReserveTargetAmount(
reserveBalance(_sourceToken),
reserves[_sourceToken].weight,
reserveBalance(_targetToken),
reserves[_targetToken].weight,
_amount
);
// return the amount minus the conversion fee and the conversion fee
uint256 fee = calculateFee(amount);
return (amount - fee, fee);
}
/**
* @dev converts a specific amount of source tokens to target tokens
* can only be called by the bancor network contract
*
* @param _sourceToken source ERC20 token
* @param _targetToken target ERC20 token
* @param _amount amount of tokens to convert (in units of the source token)
* @param _trader address of the caller who executed the conversion
* @param _beneficiary wallet to receive the conversion result
*
* @return amount of tokens received (in units of the target token)
*/
function doConvert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address _beneficiary)
internal
returns (uint256)
{
// get expected target amount and fee
(uint256 amount, uint256 fee) = targetAmountAndFee(_sourceToken, _targetToken, _amount);
// ensure that the trade gives something in return
require(amount != 0, "ERR_ZERO_TARGET_AMOUNT");
// ensure that the trade won't deplete the reserve balance
uint256 targetReserveBalance = reserveBalance(_targetToken);
assert(amount < targetReserveBalance);
// ensure that the input amount was already deposited
if (_sourceToken == ETH_RESERVE_ADDRESS)
require(msg.value == _amount, "ERR_ETH_AMOUNT_MISMATCH");
else
require(msg.value == 0 && _sourceToken.balanceOf(this).sub(reserveBalance(_sourceToken)) >= _amount, "ERR_INVALID_AMOUNT");
// sync the reserve balances
syncReserveBalance(_sourceToken);
reserves[_targetToken].balance = reserves[_targetToken].balance.sub(amount);
// transfer funds to the beneficiary in the to reserve token
if (_targetToken == ETH_RESERVE_ADDRESS)
_beneficiary.transfer(amount);
else
safeTransfer(_targetToken, _beneficiary, amount);
// dispatch the conversion event
dispatchConversionEvent(_sourceToken, _targetToken, _trader, _amount, amount, fee);
// dispatch rate updates
dispatchRateEvents(_sourceToken, _targetToken);
return amount;
}
/**
* @dev increases the pool's liquidity and mints new shares in the pool to the caller
* note that prior to version 28, you should use 'fund' instead
*
* @param _reserveTokens address of each reserve token
* @param _reserveAmounts amount of each reserve token
* @param _minReturn token minimum return-amount
*/
function addLiquidity(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _minReturn)
public
payable
protected
active
{
// verify the user input
verifyLiquidityInput(_reserveTokens, _reserveAmounts, _minReturn);
// if one of the reserves is ETH, then verify that the input amount of ETH is equal to the input value of ETH
for (uint256 i = 0; i < _reserveTokens.length; i++)
if (_reserveTokens[i] == ETH_RESERVE_ADDRESS)
require(_reserveAmounts[i] == msg.value, "ERR_ETH_AMOUNT_MISMATCH");
// if the input value of ETH is larger than zero, then verify that one of the reserves is ETH
if (msg.value > 0)
require(reserves[ETH_RESERVE_ADDRESS].isSet, "ERR_NO_ETH_RESERVE");
// get the total supply
uint256 totalSupply = ISmartToken(anchor).totalSupply();
// transfer from the user an equally-worth amount of each one of the reserve tokens
uint256 amount = addLiquidityToPool(_reserveTokens, _reserveAmounts, totalSupply);
// verify that the equivalent amount of tokens is equal to or larger than the user's expectation
require(amount >= _minReturn, "ERR_RETURN_TOO_LOW");
// issue the tokens to the user
ISmartToken(anchor).issue(msg.sender, amount);
}
/**
* @dev decreases the pool's liquidity and burns the caller's shares in the pool
* note that prior to version 28, you should use 'liquidate' instead
*
* @param _amount token amount
* @param _reserveTokens address of each reserve token
* @param _reserveMinReturnAmounts minimum return-amount of each reserve token
*/
function removeLiquidity(uint256 _amount, IERC20Token[] memory _reserveTokens, uint256[] memory _reserveMinReturnAmounts)
public
protected
active
{
// verify the user input
verifyLiquidityInput(_reserveTokens, _reserveMinReturnAmounts, _amount);
// get the total supply BEFORE destroying the user tokens
uint256 totalSupply = ISmartToken(anchor).totalSupply();
// destroy the user tokens
ISmartToken(anchor).destroy(msg.sender, _amount);
// transfer to the user an equivalent amount of each one of the reserve tokens
removeLiquidityFromPool(_reserveTokens, _reserveMinReturnAmounts, totalSupply, _amount);
}
/**
* @dev increases the pool's liquidity and mints new shares in the pool to the caller
* for example, if the caller increases the supply by 10%,
* then it will cost an amount equal to 10% of each reserve token balance
* note that starting from version 28, you should use 'addLiquidity' instead
*
* @param _amount amount to increase the supply by (in the pool token)
*/
function fund(uint256 _amount) public payable protected {
syncReserveBalances();
reserves[ETH_RESERVE_ADDRESS].balance = reserves[ETH_RESERVE_ADDRESS].balance.sub(msg.value);
uint256 supply = ISmartToken(anchor).totalSupply();
IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA));
// iterate through the reserve tokens and transfer a percentage equal to the weight between
// _amount and the total supply in each reserve from the caller to the converter
uint256 reserveCount = reserveTokens.length;
for (uint256 i = 0; i < reserveCount; i++) {
IERC20Token reserveToken = reserveTokens[i];
uint256 rsvBalance = reserves[reserveToken].balance;
uint256 reserveAmount = formula.fundCost(supply, rsvBalance, reserveRatio, _amount);
// transfer funds from the caller in the reserve token
if (reserveToken == ETH_RESERVE_ADDRESS) {
if (msg.value > reserveAmount) {
msg.sender.transfer(msg.value - reserveAmount);
}
else if (msg.value < reserveAmount) {
require(msg.value == 0, "ERR_INVALID_ETH_VALUE");
safeTransferFrom(etherToken, msg.sender, this, reserveAmount);
etherToken.withdraw(reserveAmount);
}
}
else {
safeTransferFrom(reserveToken, msg.sender, this, reserveAmount);
}
// sync the reserve balance
uint256 newReserveBalance = rsvBalance.add(reserveAmount);
reserves[reserveToken].balance = newReserveBalance;
uint256 newPoolTokenSupply = supply.add(_amount);
// dispatch liquidity update for the pool token/reserve
emit LiquidityAdded(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply);
// dispatch the `TokenRateUpdate` event for the pool token
uint32 reserveWeight = reserves[reserveToken].weight;
dispatchPoolTokenRateEvent(newPoolTokenSupply, reserveToken, newReserveBalance, reserveWeight);
}
// issue new funds to the caller in the pool token
ISmartToken(anchor).issue(msg.sender, _amount);
}
/**
* @dev decreases the pool's liquidity and burns the caller's shares in the pool
* for example, if the holder sells 10% of the supply,
* then they will receive 10% of each reserve token balance in return
* note that starting from version 28, you should use 'removeLiquidity' instead
*
* @param _amount amount to liquidate (in the pool token)
*/
function liquidate(uint256 _amount) public protected {
require(_amount > 0, "ERR_ZERO_AMOUNT");
uint256 totalSupply = ISmartToken(anchor).totalSupply();
ISmartToken(anchor).destroy(msg.sender, _amount);
uint256[] memory reserveMinReturnAmounts = new uint256[](reserveTokens.length);
for (uint256 i = 0; i < reserveMinReturnAmounts.length; i++)
reserveMinReturnAmounts[i] = 1;
removeLiquidityFromPool(reserveTokens, reserveMinReturnAmounts, totalSupply, _amount);
}
/**
* @dev verifies that a given array of tokens is identical to the converter's array of reserve tokens
* we take this input in order to allow specifying the corresponding reserve amounts in any order
*
* @param _reserveTokens array of reserve tokens
* @param _reserveAmounts array of reserve amounts
* @param _amount token amount
*/
function verifyLiquidityInput(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _amount) private view {
uint256 i;
uint256 j;
uint256 length = reserveTokens.length;
require(length == _reserveTokens.length, "ERR_INVALID_RESERVE");
require(length == _reserveAmounts.length, "ERR_INVALID_AMOUNT");
for (i = 0; i < length; i++) {
// verify that every input reserve token is included in the reserve tokens
require(reserves[_reserveTokens[i]].isSet, "ERR_INVALID_RESERVE");
for (j = 0; j < length; j++) {
if (reserveTokens[i] == _reserveTokens[j])
break;
}
// verify that every reserve token is included in the input reserve tokens
require(j < length, "ERR_INVALID_RESERVE");
// verify that every input reserve token amount is larger than zero
require(_reserveAmounts[i] > 0, "ERR_INVALID_AMOUNT");
}
// verify that the input token amount is larger than zero
require(_amount > 0, "ERR_ZERO_AMOUNT");
}
/**
* @dev adds liquidity (reserve) to the pool
*
* @param _reserveTokens address of each reserve token
* @param _reserveAmounts amount of each reserve token
* @param _totalSupply token total supply
*/
function addLiquidityToPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _totalSupply)
private
returns (uint256)
{
if (_totalSupply == 0)
return addLiquidityToEmptyPool(_reserveTokens, _reserveAmounts);
return addLiquidityToNonEmptyPool(_reserveTokens, _reserveAmounts, _totalSupply);
}
/**
* @dev adds liquidity (reserve) to the pool when it's empty
*
* @param _reserveTokens address of each reserve token
* @param _reserveAmounts amount of each reserve token
*/
function addLiquidityToEmptyPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts)
private
returns (uint256)
{
// calculate the geometric-mean of the reserve amounts approved by the user
uint256 amount = geometricMean(_reserveAmounts);
// transfer each one of the reserve amounts from the user to the pool
for (uint256 i = 0; i < _reserveTokens.length; i++) {
if (_reserveTokens[i] != ETH_RESERVE_ADDRESS) // ETH has already been transferred as part of the transaction
safeTransferFrom(_reserveTokens[i], msg.sender, this, _reserveAmounts[i]);
reserves[_reserveTokens[i]].balance = _reserveAmounts[i];
emit LiquidityAdded(msg.sender, _reserveTokens[i], _reserveAmounts[i], _reserveAmounts[i], amount);
// dispatch the `TokenRateUpdate` event for the pool token
uint32 reserveWeight = reserves[_reserveTokens[i]].weight;
dispatchPoolTokenRateEvent(amount, _reserveTokens[i], _reserveAmounts[i], reserveWeight);
}
return amount;
}
/**
* @dev adds liquidity (reserve) to the pool when it's not empty
*
* @param _reserveTokens address of each reserve token
* @param _reserveAmounts amount of each reserve token
* @param _totalSupply token total supply
*/
function addLiquidityToNonEmptyPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _totalSupply)
private
returns (uint256)
{
syncReserveBalances();
reserves[ETH_RESERVE_ADDRESS].balance = reserves[ETH_RESERVE_ADDRESS].balance.sub(msg.value);
IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA));
uint256 amount = getMinShare(formula, _totalSupply, _reserveTokens, _reserveAmounts);
uint256 newPoolTokenSupply = _totalSupply.add(amount);
for (uint256 i = 0; i < _reserveTokens.length; i++) {
IERC20Token reserveToken = _reserveTokens[i];
uint256 rsvBalance = reserves[reserveToken].balance;
uint256 reserveAmount = formula.fundCost(_totalSupply, rsvBalance, reserveRatio, amount);
require(reserveAmount > 0, "ERR_ZERO_TARGET_AMOUNT");
assert(reserveAmount <= _reserveAmounts[i]);
// transfer each one of the reserve amounts from the user to the pool
if (reserveToken != ETH_RESERVE_ADDRESS) // ETH has already been transferred as part of the transaction
safeTransferFrom(reserveToken, msg.sender, this, reserveAmount);
else if (_reserveAmounts[i] > reserveAmount) // transfer the extra amount of ETH back to the user
msg.sender.transfer(_reserveAmounts[i] - reserveAmount);
uint256 newReserveBalance = rsvBalance.add(reserveAmount);
reserves[reserveToken].balance = newReserveBalance;
emit LiquidityAdded(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply);
// dispatch the `TokenRateUpdate` event for the pool token
uint32 reserveWeight = reserves[_reserveTokens[i]].weight;
dispatchPoolTokenRateEvent(newPoolTokenSupply, _reserveTokens[i], newReserveBalance, reserveWeight);
}
return amount;
}
/**
* @dev removes liquidity (reserve) from the pool
*
* @param _reserveTokens address of each reserve token
* @param _reserveMinReturnAmounts minimum return-amount of each reserve token
* @param _totalSupply token total supply
* @param _amount token amount
*/
function removeLiquidityFromPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveMinReturnAmounts, uint256 _totalSupply, uint256 _amount)
private
{
syncReserveBalances();
IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA));
uint256 newPoolTokenSupply = _totalSupply.sub(_amount);
for (uint256 i = 0; i < _reserveTokens.length; i++) {
IERC20Token reserveToken = _reserveTokens[i];
uint256 rsvBalance = reserves[reserveToken].balance;
uint256 reserveAmount = formula.liquidateReserveAmount(_totalSupply, rsvBalance, reserveRatio, _amount);
require(reserveAmount >= _reserveMinReturnAmounts[i], "ERR_ZERO_TARGET_AMOUNT");
uint256 newReserveBalance = rsvBalance.sub(reserveAmount);
reserves[reserveToken].balance = newReserveBalance;
// transfer each one of the reserve amounts from the pool to the user
if (reserveToken == ETH_RESERVE_ADDRESS)
msg.sender.transfer(reserveAmount);
else
safeTransfer(reserveToken, msg.sender, reserveAmount);
emit LiquidityRemoved(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply);
// dispatch the `TokenRateUpdate` event for the pool token
uint32 reserveWeight = reserves[reserveToken].weight;
dispatchPoolTokenRateEvent(newPoolTokenSupply, reserveToken, newReserveBalance, reserveWeight);
}
}
function getMinShare(IBancorFormula formula, uint256 _totalSupply, IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts) private view returns (uint256) {
uint256 minIndex = 0;
for (uint256 i = 1; i < _reserveTokens.length; i++) {
if (_reserveAmounts[i].mul(reserves[_reserveTokens[minIndex]].balance) < _reserveAmounts[minIndex].mul(reserves[_reserveTokens[i]].balance))
minIndex = i;
}
return formula.fundSupplyAmount(_totalSupply, reserves[_reserveTokens[minIndex]].balance, reserveRatio, _reserveAmounts[minIndex]);
}
/**
* @dev calculates the number of decimal digits in a given value
*
* @param _x value (assumed positive)
* @return the number of decimal digits in the given value
*/
function decimalLength(uint256 _x) public pure returns (uint256) {
uint256 y = 0;
for (uint256 x = _x; x > 0; x /= 10)
y++;
return y;
}
/**
* @dev calculates the nearest integer to a given quotient
*
* @param _n quotient numerator
* @param _d quotient denominator
* @return the nearest integer to the given quotient
*/
function roundDiv(uint256 _n, uint256 _d) public pure returns (uint256) {
return (_n + _d / 2) / _d;
}
/**
* @dev calculates the average number of decimal digits in a given list of values
*
* @param _values list of values (each of which assumed positive)
* @return the average number of decimal digits in the given list of values
*/
function geometricMean(uint256[] memory _values) public pure returns (uint256) {
uint256 numOfDigits = 0;
uint256 length = _values.length;
for (uint256 i = 0; i < length; i++)
numOfDigits += decimalLength(_values[i]);
return uint256(10) ** (roundDiv(numOfDigits, length) - 1);
}
/**
* @dev dispatches rate events for both reserves / pool tokens
* only used to circumvent the `stack too deep` compiler error
*
* @param _sourceToken address of the source reserve token
* @param _targetToken address of the target reserve token
*/
function dispatchRateEvents(IERC20Token _sourceToken, IERC20Token _targetToken) private {
uint256 poolTokenSupply = ISmartToken(anchor).totalSupply();
uint256 sourceReserveBalance = reserveBalance(_sourceToken);
uint256 targetReserveBalance = reserveBalance(_targetToken);
uint32 sourceReserveWeight = reserves[_sourceToken].weight;
uint32 targetReserveWeight = reserves[_targetToken].weight;
// dispatch token rate update event
uint256 rateN = targetReserveBalance.mul(sourceReserveWeight);
uint256 rateD = sourceReserveBalance.mul(targetReserveWeight);
emit TokenRateUpdate(_sourceToken, _targetToken, rateN, rateD);
// dispatch the `TokenRateUpdate` event for the pool token
dispatchPoolTokenRateEvent(poolTokenSupply, _sourceToken, sourceReserveBalance, sourceReserveWeight);
dispatchPoolTokenRateEvent(poolTokenSupply, _targetToken, targetReserveBalance, targetReserveWeight);
// dispatch price data update events (deprecated events)
emit PriceDataUpdate(_sourceToken, poolTokenSupply, sourceReserveBalance, sourceReserveWeight);
emit PriceDataUpdate(_targetToken, poolTokenSupply, targetReserveBalance, targetReserveWeight);
}
/**
* @dev dispatches the `TokenRateUpdate` for the pool token
* only used to circumvent the `stack too deep` compiler error
*
* @param _poolTokenSupply total pool token supply
* @param _reserveToken address of the reserve token
* @param _reserveBalance reserve balance
* @param _reserveWeight reserve weight
*/
function dispatchPoolTokenRateEvent(uint256 _poolTokenSupply, IERC20Token _reserveToken, uint256 _reserveBalance, uint32 _reserveWeight) private {
emit TokenRateUpdate(anchor, _reserveToken, _reserveBalance.mul(WEIGHT_RESOLUTION), _poolTokenSupply.mul(_reserveWeight));
}
}File 4 of 8: SmartToken
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;
}
}
/*
Owned contract interface
*/
contract IOwned {
// this function isn't abstract since the compiler emits automatically generated getter functions as external
function owner() public constant returns (address owner) { owner; }
function transferOwnership(address _newOwner) public;
function acceptOwnership() public;
}
/*
Provides support and utilities for contract ownership
*/
contract Owned is IOwned {
address public owner;
address public newOwner;
event OwnerUpdate(address _prevOwner, address _newOwner);
/**
@dev constructor
*/
function Owned() {
owner = msg.sender;
}
// allows execution by the owner only
modifier ownerOnly {
assert(msg.sender == owner);
_;
}
/**
@dev allows transferring the contract ownership
the new owner still need to accept the transfer
can only be called by the contract owner
@param _newOwner new contract owner
*/
function transferOwnership(address _newOwner) public ownerOnly {
require(_newOwner != owner);
newOwner = _newOwner;
}
/**
@dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() public {
require(msg.sender == newOwner);
OwnerUpdate(owner, newOwner);
owner = newOwner;
newOwner = 0x0;
}
}
/*
Token Holder interface
*/
contract ITokenHolder is IOwned {
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
}
/*
We consider every contract to be a 'token holder' since it's currently not possible
for a contract to deny receiving tokens.
The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
the owner to send tokens that were sent to the contract by mistake back to their sender.
*/
contract TokenHolder is ITokenHolder, Owned {
/**
@dev constructor
*/
function TokenHolder() {
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address _address) {
require(_address != 0x0);
_;
}
// verifies that the address is different than this contract address
modifier notThis(address _address) {
require(_address != address(this));
_;
}
/**
@dev withdraws tokens held by the contract and sends them to an account
can only be called by the owner
@param _token ERC20 token contract address
@param _to account to receive the new amount
@param _amount amount to withdraw
*/
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
public
ownerOnly
validAddress(_token)
validAddress(_to)
notThis(_to)
{
assert(_token.transfer(_to, _amount));
}
}
/*
ERC20 Standard Token interface
*/
contract IERC20Token {
// these functions aren't abstract since the compiler emits automatically generated getter functions as external
function name() public constant returns (string name) { name; }
function symbol() public constant returns (string symbol) { symbol; }
function decimals() public constant returns (uint8 decimals) { decimals; }
function totalSupply() public constant returns (uint256 totalSupply) { totalSupply; }
function balanceOf(address _owner) public constant returns (uint256 balance) { _owner; balance; }
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) { _owner; _spender; remaining; }
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
}
/**
ERC20 Standard Token implementation
*/
contract ERC20Token is IERC20Token, SafeMath {
string public standard = 'Token 0.1';
string public name = '';
string public symbol = '';
uint8 public decimals = 0;
uint256 public totalSupply = 0;
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
/**
@dev constructor
@param _name token name
@param _symbol token symbol
@param _decimals decimal points, for display purposes
*/
function ERC20Token(string _name, string _symbol, uint8 _decimals) {
require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input
name = _name;
symbol = _symbol;
decimals = _decimals;
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address _address) {
require(_address != 0x0);
_;
}
/**
@dev send coins
throws on any error rather then return a false flag to minimize user errors
@param _to target address
@param _value transfer amount
@return true if the transfer was successful, false if it wasn't
*/
function transfer(address _to, uint256 _value)
public
validAddress(_to)
returns (bool success)
{
balanceOf[msg.sender] = safeSub(balanceOf[msg.sender], _value);
balanceOf[_to] = safeAdd(balanceOf[_to], _value);
Transfer(msg.sender, _to, _value);
return true;
}
/**
@dev an account/contract attempts to get the coins
throws on any error rather then return a false flag to minimize user errors
@param _from source address
@param _to target address
@param _value transfer amount
@return true if the transfer was successful, false if it wasn't
*/
function transferFrom(address _from, address _to, uint256 _value)
public
validAddress(_from)
validAddress(_to)
returns (bool success)
{
allowance[_from][msg.sender] = safeSub(allowance[_from][msg.sender], _value);
balanceOf[_from] = safeSub(balanceOf[_from], _value);
balanceOf[_to] = safeAdd(balanceOf[_to], _value);
Transfer(_from, _to, _value);
return true;
}
/**
@dev allow another account/contract to spend some tokens on your behalf
throws on any error rather then return a false flag to minimize user errors
also, to minimize the risk of the approve/transferFrom attack vector
(see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value
@param _spender approved address
@param _value allowance amount
@return true if the approval was successful, false if it wasn't
*/
function approve(address _spender, uint256 _value)
public
validAddress(_spender)
returns (bool success)
{
// if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
require(_value == 0 || allowance[msg.sender][_spender] == 0);
allowance[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
}
/*
Smart Token interface
*/
contract ISmartToken is ITokenHolder, IERC20Token {
function disableTransfers(bool _disable) public;
function issue(address _to, uint256 _amount) public;
function destroy(address _from, uint256 _amount) public;
}
/*
Smart Token v0.2
'Owned' is specified here for readability reasons
*/
contract SmartToken is ISmartToken, ERC20Token, Owned, TokenHolder {
string public version = '0.2';
bool public transfersEnabled = true; // true if transfer/transferFrom are enabled, false if not
// triggered when a smart token is deployed - the _token address is defined for forward compatibility, in case we want to trigger the event from a factory
event NewSmartToken(address _token);
// triggered when the total supply is increased
event Issuance(uint256 _amount);
// triggered when the total supply is decreased
event Destruction(uint256 _amount);
/**
@dev constructor
@param _name token name
@param _symbol token short symbol, 1-6 characters
@param _decimals for display purposes only
*/
function SmartToken(string _name, string _symbol, uint8 _decimals)
ERC20Token(_name, _symbol, _decimals)
{
require(bytes(_symbol).length <= 6); // validate input
NewSmartToken(address(this));
}
// allows execution only when transfers aren't disabled
modifier transfersAllowed {
assert(transfersEnabled);
_;
}
/**
@dev disables/enables transfers
can only be called by the contract owner
@param _disable true to disable transfers, false to enable them
*/
function disableTransfers(bool _disable) public ownerOnly {
transfersEnabled = !_disable;
}
/**
@dev increases the token supply and sends the new tokens to an account
can only be called by the contract owner
@param _to account to receive the new amount
@param _amount amount to increase the supply by
*/
function issue(address _to, uint256 _amount)
public
ownerOnly
validAddress(_to)
notThis(_to)
{
totalSupply = safeAdd(totalSupply, _amount);
balanceOf[_to] = safeAdd(balanceOf[_to], _amount);
Issuance(_amount);
Transfer(this, _to, _amount);
}
/**
@dev removes tokens from an account and decreases the token supply
can only be called by the contract owner
@param _from account to remove the amount from
@param _amount amount to decrease the supply by
*/
function destroy(address _from, uint256 _amount)
public
ownerOnly
{
balanceOf[_from] = safeSub(balanceOf[_from], _amount);
totalSupply = safeSub(totalSupply, _amount);
Transfer(_from, this, _amount);
Destruction(_amount);
}
// ERC20 standard method overrides with some extra functionality
/**
@dev send coins
throws on any error rather then return a false flag to minimize user errors
note that when transferring to the smart token's address, the coins are actually destroyed
@param _to target address
@param _value transfer amount
@return true if the transfer was successful, false if it wasn't
*/
function transfer(address _to, uint256 _value) public transfersAllowed returns (bool success) {
assert(super.transfer(_to, _value));
// transferring to the contract address destroys tokens
if (_to == address(this)) {
balanceOf[_to] -= _value;
totalSupply -= _value;
Destruction(_value);
}
return true;
}
/**
@dev an account/contract attempts to get the coins
throws on any error rather then return a false flag to minimize user errors
note that when transferring to the smart token's address, the coins are actually destroyed
@param _from source address
@param _to target address
@param _value transfer amount
@return true if the transfer was successful, false if it wasn't
*/
function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed returns (bool success) {
assert(super.transferFrom(_from, _to, _value));
// transferring to the contract address destroys tokens
if (_to == address(this)) {
balanceOf[_to] -= _value;
totalSupply -= _value;
Destruction(_value);
}
return true;
}
}File 5 of 8: SmartToken
pragma solidity ^0.4.11;
/*
Utilities & Common Modifiers
*/
contract Utils {
/**
constructor
*/
function Utils() {
}
// verifies that an amount is greater than zero
modifier greaterThanZero(uint256 _amount) {
require(_amount > 0);
_;
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address _address) {
require(_address != 0x0);
_;
}
// verifies that the address is different than this contract address
modifier notThis(address _address) {
require(_address != address(this));
_;
}
// Overflow protected math functions
/**
@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 constant 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 constant 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 constant returns (uint256) {
uint256 z = _x * _y;
assert(_x == 0 || z / _x == _y);
return z;
}
}
/*
Owned contract interface
*/
contract IOwned {
// this function isn't abstract since the compiler emits automatically generated getter functions as external
function owner() public constant returns (address) {}
function transferOwnership(address _newOwner) public;
function acceptOwnership() public;
}
/*
Provides support and utilities for contract ownership
*/
contract Owned is IOwned {
address public owner;
address public newOwner;
event OwnerUpdate(address _prevOwner, address _newOwner);
/**
@dev constructor
*/
function Owned() {
owner = msg.sender;
}
// allows execution by the owner only
modifier ownerOnly {
assert(msg.sender == owner);
_;
}
/**
@dev allows transferring the contract ownership
the new owner still needs to accept the transfer
can only be called by the contract owner
@param _newOwner new contract owner
*/
function transferOwnership(address _newOwner) public ownerOnly {
require(_newOwner != owner);
newOwner = _newOwner;
}
/**
@dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() public {
require(msg.sender == newOwner);
OwnerUpdate(owner, newOwner);
owner = newOwner;
newOwner = 0x0;
}
}
/*
ERC20 Standard Token interface
*/
contract IERC20Token {
// these functions aren't abstract since the compiler emits automatically generated getter functions as external
function name() public constant returns (string) {}
function symbol() public constant returns (string) {}
function decimals() public constant returns (uint8) {}
function totalSupply() public constant returns (uint256) {}
function balanceOf(address _owner) public constant returns (uint256) { _owner; }
function allowance(address _owner, address _spender) public constant returns (uint256) { _owner; _spender; }
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
}
/**
ERC20 Standard Token implementation
*/
contract ERC20Token is IERC20Token, Utils {
string public standard = 'Token 0.1';
string public name = '';
string public symbol = '';
uint8 public decimals = 0;
uint256 public totalSupply = 0;
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
/**
@dev constructor
@param _name token name
@param _symbol token symbol
@param _decimals decimal points, for display purposes
*/
function ERC20Token(string _name, string _symbol, uint8 _decimals) {
require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input
name = _name;
symbol = _symbol;
decimals = _decimals;
}
/**
@dev send coins
throws on any error rather then return a false flag to minimize user errors
@param _to target address
@param _value transfer amount
@return true if the transfer was successful, false if it wasn't
*/
function transfer(address _to, uint256 _value)
public
validAddress(_to)
returns (bool success)
{
balanceOf[msg.sender] = safeSub(balanceOf[msg.sender], _value);
balanceOf[_to] = safeAdd(balanceOf[_to], _value);
Transfer(msg.sender, _to, _value);
return true;
}
/**
@dev an account/contract attempts to get the coins
throws on any error rather then return a false flag to minimize user errors
@param _from source address
@param _to target address
@param _value transfer amount
@return true if the transfer was successful, false if it wasn't
*/
function transferFrom(address _from, address _to, uint256 _value)
public
validAddress(_from)
validAddress(_to)
returns (bool success)
{
allowance[_from][msg.sender] = safeSub(allowance[_from][msg.sender], _value);
balanceOf[_from] = safeSub(balanceOf[_from], _value);
balanceOf[_to] = safeAdd(balanceOf[_to], _value);
Transfer(_from, _to, _value);
return true;
}
/**
@dev allow another account/contract to spend some tokens on your behalf
throws on any error rather then return a false flag to minimize user errors
also, to minimize the risk of the approve/transferFrom attack vector
(see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value
@param _spender approved address
@param _value allowance amount
@return true if the approval was successful, false if it wasn't
*/
function approve(address _spender, uint256 _value)
public
validAddress(_spender)
returns (bool success)
{
// if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
require(_value == 0 || allowance[msg.sender][_spender] == 0);
allowance[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
}
/*
Token Holder interface
*/
contract ITokenHolder is IOwned {
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
}
/*
We consider every contract to be a 'token holder' since it's currently not possible
for a contract to deny receiving tokens.
The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
the owner to send tokens that were sent to the contract by mistake back to their sender.
*/
contract TokenHolder is ITokenHolder, Owned, Utils {
/**
@dev constructor
*/
function TokenHolder() {
}
/**
@dev withdraws tokens held by the contract and sends them to an account
can only be called by the owner
@param _token ERC20 token contract address
@param _to account to receive the new amount
@param _amount amount to withdraw
*/
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
public
ownerOnly
validAddress(_token)
validAddress(_to)
notThis(_to)
{
assert(_token.transfer(_to, _amount));
}
}
/*
Smart Token interface
*/
contract ISmartToken is IOwned, IERC20Token {
function disableTransfers(bool _disable) public;
function issue(address _to, uint256 _amount) public;
function destroy(address _from, uint256 _amount) public;
}
/*
Smart Token v0.3
'Owned' is specified here for readability reasons
*/
contract SmartToken is ISmartToken, Owned, ERC20Token, TokenHolder {
string public version = '0.3';
bool public transfersEnabled = true; // true if transfer/transferFrom are enabled, false if not
// triggered when a smart token is deployed - the _token address is defined for forward compatibility, in case we want to trigger the event from a factory
event NewSmartToken(address _token);
// triggered when the total supply is increased
event Issuance(uint256 _amount);
// triggered when the total supply is decreased
event Destruction(uint256 _amount);
/**
@dev constructor
@param _name token name
@param _symbol token short symbol, minimum 1 character
@param _decimals for display purposes only
*/
function SmartToken(string _name, string _symbol, uint8 _decimals)
ERC20Token(_name, _symbol, _decimals)
{
NewSmartToken(address(this));
}
// allows execution only when transfers aren't disabled
modifier transfersAllowed {
assert(transfersEnabled);
_;
}
/**
@dev disables/enables transfers
can only be called by the contract owner
@param _disable true to disable transfers, false to enable them
*/
function disableTransfers(bool _disable) public ownerOnly {
transfersEnabled = !_disable;
}
/**
@dev increases the token supply and sends the new tokens to an account
can only be called by the contract owner
@param _to account to receive the new amount
@param _amount amount to increase the supply by
*/
function issue(address _to, uint256 _amount)
public
ownerOnly
validAddress(_to)
notThis(_to)
{
totalSupply = safeAdd(totalSupply, _amount);
balanceOf[_to] = safeAdd(balanceOf[_to], _amount);
Issuance(_amount);
Transfer(this, _to, _amount);
}
/**
@dev removes tokens from an account and decreases the token supply
can be called by the contract owner to destroy tokens from any account or by any holder to destroy tokens from his/her own account
@param _from account to remove the amount from
@param _amount amount to decrease the supply by
*/
function destroy(address _from, uint256 _amount) public {
require(msg.sender == _from || msg.sender == owner); // validate input
balanceOf[_from] = safeSub(balanceOf[_from], _amount);
totalSupply = safeSub(totalSupply, _amount);
Transfer(_from, this, _amount);
Destruction(_amount);
}
// ERC20 standard method overrides with some extra functionality
/**
@dev send coins
throws on any error rather then return a false flag to minimize user errors
in addition to the standard checks, the function throws if transfers are disabled
@param _to target address
@param _value transfer amount
@return true if the transfer was successful, false if it wasn't
*/
function transfer(address _to, uint256 _value) public transfersAllowed returns (bool success) {
assert(super.transfer(_to, _value));
return true;
}
/**
@dev an account/contract attempts to get the coins
throws on any error rather then return a false flag to minimize user errors
in addition to the standard checks, the function throws if transfers are disabled
@param _from source address
@param _to target address
@param _value transfer amount
@return true if the transfer was successful, false if it wasn't
*/
function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed returns (bool success) {
assert(super.transferFrom(_from, _to, _value));
return true;
}
}File 6 of 8: ContractRegistry
pragma solidity ^0.4.24;
// File: contracts/utility/interfaces/IOwned.sol
/*
Owned contract interface
*/
contract IOwned {
// this function isn't abstract since the compiler emits automatically generated getter functions as external
function owner() public view returns (address) {}
function transferOwnership(address _newOwner) public;
function acceptOwnership() public;
}
// File: contracts/utility/Owned.sol
/*
Provides support and utilities for contract ownership
*/
contract Owned is IOwned {
address public owner;
address public newOwner;
event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
/**
@dev constructor
*/
constructor() public {
owner = msg.sender;
}
// allows execution by the owner only
modifier ownerOnly {
require(msg.sender == owner);
_;
}
/**
@dev allows transferring the contract ownership
the new owner still needs to accept the transfer
can only be called by the contract owner
@param _newOwner new contract owner
*/
function transferOwnership(address _newOwner) public ownerOnly {
require(_newOwner != owner);
newOwner = _newOwner;
}
/**
@dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() public {
require(msg.sender == newOwner);
emit OwnerUpdate(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
// File: contracts/utility/Utils.sol
/*
Utilities & Common Modifiers
*/
contract Utils {
/**
constructor
*/
constructor() public {
}
// verifies that an amount is greater than zero
modifier greaterThanZero(uint256 _amount) {
require(_amount > 0);
_;
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address _address) {
require(_address != address(0));
_;
}
// verifies that the address is different than this contract address
modifier notThis(address _address) {
require(_address != address(this));
_;
}
// Overflow protected math functions
/**
@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 pure 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 pure 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 pure returns (uint256) {
uint256 z = _x * _y;
assert(_x == 0 || z / _x == _y);
return z;
}
}
// File: contracts/utility/interfaces/IContractRegistry.sol
/*
Contract Registry interface
*/
contract IContractRegistry {
function addressOf(bytes32 _contractName) public view returns (address);
// deprecated, backward compatibility
function getAddress(bytes32 _contractName) public view returns (address);
}
// File: contracts/ContractIds.sol
/**
Id definitions for bancor contracts
Can be used in conjunction with the contract registry to get contract addresses
*/
contract ContractIds {
// generic
bytes32 public constant CONTRACT_FEATURES = "ContractFeatures";
bytes32 public constant CONTRACT_REGISTRY = "ContractRegistry";
// bancor logic
bytes32 public constant BANCOR_NETWORK = "BancorNetwork";
bytes32 public constant BANCOR_FORMULA = "BancorFormula";
bytes32 public constant BANCOR_GAS_PRICE_LIMIT = "BancorGasPriceLimit";
bytes32 public constant BANCOR_CONVERTER_UPGRADER = "BancorConverterUpgrader";
bytes32 public constant BANCOR_CONVERTER_FACTORY = "BancorConverterFactory";
// Ids of BNT converter and BNT token
bytes32 public constant BNT_TOKEN = "BNTToken";
bytes32 public constant BNT_CONVERTER = "BNTConverter";
// Id of BancorX contract
bytes32 public constant BANCOR_X = "BancorX";
}
// File: contracts/utility/ContractRegistry.sol
/**
Contract Registry
The contract registry keeps contract addresses by name.
The owner can update contract addresses so that a contract name always points to the latest version
of the given contract.
Other contracts can query the registry to get updated addresses instead of depending on specific
addresses.
Note that contract names are limited to 32 bytes UTF8 encoded ASCII strings to optimize gas costs
*/
contract ContractRegistry is IContractRegistry, Owned, Utils, ContractIds {
struct RegistryItem {
address contractAddress; // contract address
uint256 nameIndex; // index of the item in the list of contract names
bool isSet; // used to tell if the mapping element is defined
}
mapping (bytes32 => RegistryItem) private items; // name -> RegistryItem mapping
string[] public contractNames; // list of all registered contract names
// triggered when an address pointed to by a contract name is modified
event AddressUpdate(bytes32 indexed _contractName, address _contractAddress);
/**
@dev constructor
*/
constructor() public {
registerAddress(ContractIds.CONTRACT_REGISTRY, address(this));
}
/**
@dev returns the number of items in the registry
@return number of items
*/
function itemCount() public view returns (uint256) {
return contractNames.length;
}
/**
@dev returns the address associated with the given contract name
@param _contractName contract name
@return contract address
*/
function addressOf(bytes32 _contractName) public view returns (address) {
return items[_contractName].contractAddress;
}
/**
@dev registers a new address for the contract name in the registry
@param _contractName contract name
@param _contractAddress contract address
*/
function registerAddress(bytes32 _contractName, address _contractAddress)
public
ownerOnly
validAddress(_contractAddress)
{
require(_contractName.length > 0); // validate input
// update the address in the registry
items[_contractName].contractAddress = _contractAddress;
if (!items[_contractName].isSet) {
// mark the item as set
items[_contractName].isSet = true;
// add the contract name to the name list
uint256 i = contractNames.push(bytes32ToString(_contractName));
// update the item's index in the list
items[_contractName].nameIndex = i - 1;
}
// dispatch the address update event
emit AddressUpdate(_contractName, _contractAddress);
}
/**
@dev removes an existing contract address from the registry
@param _contractName contract name
*/
function unregisterAddress(bytes32 _contractName) public ownerOnly {
require(_contractName.length > 0); // validate input
// remove the address from the registry
items[_contractName].contractAddress = address(0);
// if there are multiple items in the registry, move the last element to the deleted element's position
// and modify last element's registryItem.nameIndex in the items collection to point to the right position in contractNames
if (contractNames.length > 1) {
string memory lastContractNameString = contractNames[contractNames.length - 1];
uint256 unregisterIndex = items[_contractName].nameIndex;
contractNames[unregisterIndex] = lastContractNameString;
bytes32 lastContractName = stringToBytes32(lastContractNameString);
RegistryItem storage registryItem = items[lastContractName];
registryItem.nameIndex = unregisterIndex;
}
// remove the last element from the name list
contractNames.length--;
// zero the deleted element's index
items[_contractName].nameIndex = 0;
// dispatch the address update event
emit AddressUpdate(_contractName, address(0));
}
/**
@dev utility, converts bytes32 to a string
note that the bytes32 argument is assumed to be UTF8 encoded ASCII string
@return string representation of the given bytes32 argument
*/
function bytes32ToString(bytes32 _bytes) private pure returns (string) {
bytes memory byteArray = new bytes(32);
for (uint256 i; i < 32; i++) {
byteArray[i] = _bytes[i];
}
return string(byteArray);
}
// @dev utility, converts string to bytes32
function stringToBytes32(string memory _string) private pure returns (bytes32) {
bytes32 result;
assembly {
result := mload(add(_string,32))
}
return result;
}
// deprecated, backward compatibility
function getAddress(bytes32 _contractName) public view returns (address) {
return addressOf(_contractName);
}
}File 7 of 8: SmartToken
pragma solidity 0.4.26;
// File: contracts/token/interfaces/IERC20Token.sol
/*
ERC20 Standard Token interface
*/
contract IERC20Token {
// these functions aren't abstract since the compiler emits automatically generated getter functions as external
function name() public view returns (string) {this;}
function symbol() public view returns (string) {this;}
function decimals() public view returns (uint8) {this;}
function totalSupply() public view returns (uint256) {this;}
function balanceOf(address _owner) public view returns (uint256) {_owner; this;}
function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;}
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
}
// File: contracts/utility/Utils.sol
/**
* @dev Utilities & Common Modifiers
*/
contract Utils {
/**
* constructor
*/
constructor() public {
}
// verifies that an amount is greater than zero
modifier greaterThanZero(uint256 _amount) {
require(_amount > 0);
_;
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address _address) {
require(_address != address(0));
_;
}
// verifies that the address is different than this contract address
modifier notThis(address _address) {
require(_address != address(this));
_;
}
}
// File: contracts/utility/SafeMath.sol
/**
* @dev Library for basic math operations with overflow/underflow protection
*/
library SafeMath {
/**
* @dev returns the sum of _x and _y, reverts if the calculation overflows
*
* @param _x value 1
* @param _y value 2
*
* @return sum
*/
function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
uint256 z = _x + _y;
require(z >= _x);
return z;
}
/**
* @dev returns the difference of _x minus _y, reverts if the calculation underflows
*
* @param _x minuend
* @param _y subtrahend
*
* @return difference
*/
function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_x >= _y);
return _x - _y;
}
/**
* @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
*
* @param _x factor 1
* @param _y factor 2
*
* @return product
*/
function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
// gas optimization
if (_x == 0)
return 0;
uint256 z = _x * _y;
require(z / _x == _y);
return z;
}
/**
* ev Integer division of two numbers truncating the quotient, reverts on division by zero.
*
* aram _x dividend
* aram _y divisor
*
* eturn quotient
*/
function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_y > 0);
uint256 c = _x / _y;
return c;
}
}
// File: contracts/token/ERC20Token.sol
/**
* @dev ERC20 Standard Token implementation
*/
contract ERC20Token is IERC20Token, Utils {
using SafeMath for uint256;
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
/**
* @dev triggered when tokens are transferred between wallets
*
* @param _from source address
* @param _to target address
* @param _value transfer amount
*/
event Transfer(address indexed _from, address indexed _to, uint256 _value);
/**
* @dev triggered when a wallet allows another wallet to transfer tokens from on its behalf
*
* @param _owner wallet that approves the allowance
* @param _spender wallet that receives the allowance
* @param _value allowance amount
*/
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
/**
* @dev initializes a new ERC20Token instance
*
* @param _name token name
* @param _symbol token symbol
* @param _decimals decimal points, for display purposes
* @param _totalSupply total supply of token units
*/
constructor(string _name, string _symbol, uint8 _decimals, uint256 _totalSupply) public {
require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input
name = _name;
symbol = _symbol;
decimals = _decimals;
totalSupply = _totalSupply;
balanceOf[msg.sender] = _totalSupply;
}
/**
* @dev send coins
* throws on any error rather then return a false flag to minimize user errors
*
* @param _to target address
* @param _value transfer amount
*
* @return true if the transfer was successful, false if it wasn't
*/
function transfer(address _to, uint256 _value)
public
validAddress(_to)
returns (bool success)
{
balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value);
balanceOf[_to] = balanceOf[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev an account/contract attempts to get the coins
* throws on any error rather then return a false flag to minimize user errors
*
* @param _from source address
* @param _to target address
* @param _value transfer amount
*
* @return true if the transfer was successful, false if it wasn't
*/
function transferFrom(address _from, address _to, uint256 _value)
public
validAddress(_from)
validAddress(_to)
returns (bool success)
{
allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value);
balanceOf[_from] = balanceOf[_from].sub(_value);
balanceOf[_to] = balanceOf[_to].add(_value);
emit Transfer(_from, _to, _value);
return true;
}
/**
* @dev allow another account/contract to spend some tokens on your behalf
* throws on any error rather then return a false flag to minimize user errors
*
* also, to minimize the risk of the approve/transferFrom attack vector
* (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
* in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value
*
* @param _spender approved address
* @param _value allowance amount
*
* @return true if the approval was successful, false if it wasn't
*/
function approve(address _spender, uint256 _value)
public
validAddress(_spender)
returns (bool success)
{
// if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
require(_value == 0 || allowance[msg.sender][_spender] == 0);
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
}
// File: contracts/utility/interfaces/IOwned.sol
/*
Owned contract interface
*/
contract IOwned {
// this function isn't abstract since the compiler emits automatically generated getter functions as external
function owner() public view returns (address) {this;}
function transferOwnership(address _newOwner) public;
function acceptOwnership() public;
}
// File: contracts/token/interfaces/ISmartToken.sol
/*
Smart Token interface
*/
contract ISmartToken is IOwned, IERC20Token {
function disableTransfers(bool _disable) public;
function issue(address _to, uint256 _amount) public;
function destroy(address _from, uint256 _amount) public;
}
// File: contracts/utility/Owned.sol
/**
* @dev Provides support and utilities for contract ownership
*/
contract Owned is IOwned {
address public owner;
address public newOwner;
/**
* @dev triggered when the owner is updated
*
* @param _prevOwner previous owner
* @param _newOwner new owner
*/
event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
/**
* @dev initializes a new Owned instance
*/
constructor() public {
owner = msg.sender;
}
// allows execution by the owner only
modifier ownerOnly {
require(msg.sender == owner);
_;
}
/**
* @dev allows transferring the contract ownership
* the new owner still needs to accept the transfer
* can only be called by the contract owner
*
* @param _newOwner new contract owner
*/
function transferOwnership(address _newOwner) public ownerOnly {
require(_newOwner != owner);
newOwner = _newOwner;
}
/**
* @dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() public {
require(msg.sender == newOwner);
emit OwnerUpdate(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
// File: contracts/utility/interfaces/ITokenHolder.sol
/*
Token Holder interface
*/
contract ITokenHolder is IOwned {
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
}
// File: contracts/token/interfaces/INonStandardERC20.sol
/*
ERC20 Standard Token interface which doesn't return true/false for transfer, transferFrom and approve
*/
contract INonStandardERC20 {
// these functions aren't abstract since the compiler emits automatically generated getter functions as external
function name() public view returns (string) {this;}
function symbol() public view returns (string) {this;}
function decimals() public view returns (uint8) {this;}
function totalSupply() public view returns (uint256) {this;}
function balanceOf(address _owner) public view returns (uint256) {_owner; this;}
function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;}
function transfer(address _to, uint256 _value) public;
function transferFrom(address _from, address _to, uint256 _value) public;
function approve(address _spender, uint256 _value) public;
}
// File: contracts/utility/TokenHolder.sol
/**
* @dev We consider every contract to be a 'token holder' since it's currently not possible
* for a contract to deny receiving tokens.
*
* The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
* the owner to send tokens that were sent to the contract by mistake back to their sender.
*
* Note that we use the non standard ERC-20 interface which has no return value for transfer
* in order to support both non standard as well as standard token contracts.
* see https://github.com/ethereum/solidity/issues/4116
*/
contract TokenHolder is ITokenHolder, Owned, Utils {
/**
* @dev initializes a new TokenHolder instance
*/
constructor() public {
}
/**
* @dev withdraws tokens held by the contract and sends them to an account
* can only be called by the owner
*
* @param _token ERC20 token contract address
* @param _to account to receive the new amount
* @param _amount amount to withdraw
*/
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
public
ownerOnly
validAddress(_token)
validAddress(_to)
notThis(_to)
{
INonStandardERC20(_token).transfer(_to, _amount);
}
}
// File: contracts/token/SmartToken.sol
/**
* @dev Smart Token
*
* 'Owned' is specified here for readability reasons
*/
contract SmartToken is ISmartToken, Owned, ERC20Token, TokenHolder {
using SafeMath for uint256;
string public version = '0.3';
bool public transfersEnabled = true; // true if transfer/transferFrom are enabled, false if not
/**
* @dev triggered when a smart token is deployed
* the _token address is defined for forward compatibility, in case the event is trigger by a factory
*
* @param _token new smart token address
*/
event NewSmartToken(address _token);
/**
* @dev triggered when the total supply is increased
*
* @param _amount amount that gets added to the supply
*/
event Issuance(uint256 _amount);
/**
* @dev triggered when the total supply is decreased
*
* @param _amount amount that gets removed from the supply
*/
event Destruction(uint256 _amount);
/**
* @dev initializes a new SmartToken instance
*
* @param _name token name
* @param _symbol token short symbol, minimum 1 character
* @param _decimals for display purposes only
*/
constructor(string _name, string _symbol, uint8 _decimals)
public
ERC20Token(_name, _symbol, _decimals, 0)
{
emit NewSmartToken(address(this));
}
// allows execution only when transfers aren't disabled
modifier transfersAllowed {
assert(transfersEnabled);
_;
}
/**
* @dev disables/enables transfers
* can only be called by the contract owner
*
* @param _disable true to disable transfers, false to enable them
*/
function disableTransfers(bool _disable) public ownerOnly {
transfersEnabled = !_disable;
}
/**
* @dev increases the token supply and sends the new tokens to an account
* can only be called by the contract owner
*
* @param _to account to receive the new amount
* @param _amount amount to increase the supply by
*/
function issue(address _to, uint256 _amount)
public
ownerOnly
validAddress(_to)
notThis(_to)
{
totalSupply = totalSupply.add(_amount);
balanceOf[_to] = balanceOf[_to].add(_amount);
emit Issuance(_amount);
emit Transfer(this, _to, _amount);
}
/**
* @dev removes tokens from an account and decreases the token supply
* can be called by the contract owner to destroy tokens from any account or by any holder to destroy tokens from his/her own account
*
* @param _from account to remove the amount from
* @param _amount amount to decrease the supply by
*/
function destroy(address _from, uint256 _amount) public {
require(msg.sender == _from || msg.sender == owner); // validate input
balanceOf[_from] = balanceOf[_from].sub(_amount);
totalSupply = totalSupply.sub(_amount);
emit Transfer(_from, this, _amount);
emit Destruction(_amount);
}
// ERC20 standard method overrides with some extra functionality
/**
* @dev send coins
* throws on any error rather then return a false flag to minimize user errors
* in addition to the standard checks, the function throws if transfers are disabled
*
* @param _to target address
* @param _value transfer amount
*
* @return true if the transfer was successful, false if it wasn't
*/
function transfer(address _to, uint256 _value) public transfersAllowed returns (bool success) {
assert(super.transfer(_to, _value));
return true;
}
/**
* @dev an account/contract attempts to get the coins
* throws on any error rather then return a false flag to minimize user errors
* in addition to the standard checks, the function throws if transfers are disabled
*
* @param _from source address
* @param _to target address
* @param _value transfer amount
*
* @return true if the transfer was successful, false if it wasn't
*/
function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed returns (bool success) {
assert(super.transferFrom(_from, _to, _value));
return true;
}
}
File 8 of 8: BancorFormula
// File: solidity/contracts/converter/interfaces/IBancorFormula.sol
pragma solidity 0.4.26;
/*
Bancor Formula interface
*/
contract IBancorFormula {
function purchaseTargetAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256);
function saleTargetAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256);
function crossReserveTargetAmount(uint256 _sourceReserveBalance,
uint32 _sourceReserveWeight,
uint256 _targetReserveBalance,
uint32 _targetReserveWeight,
uint256 _amount)
public view returns (uint256);
function fundCost(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256);
function fundSupplyAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256);
function liquidateReserveAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256);
function balancedWeights(uint256 _primaryReserveStakedBalance,
uint256 _primaryReserveBalance,
uint256 _secondaryReserveBalance,
uint256 _reserveRateNumerator,
uint256 _reserveRateDenominator)
public view returns (uint32, uint32);
}
// File: solidity/contracts/utility/SafeMath.sol
pragma solidity 0.4.26;
/**
* @dev Library for basic math operations with overflow/underflow protection
*/
library SafeMath {
/**
* @dev returns the sum of _x and _y, reverts if the calculation overflows
*
* @param _x value 1
* @param _y value 2
*
* @return sum
*/
function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
uint256 z = _x + _y;
require(z >= _x, "ERR_OVERFLOW");
return z;
}
/**
* @dev returns the difference of _x minus _y, reverts if the calculation underflows
*
* @param _x minuend
* @param _y subtrahend
*
* @return difference
*/
function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_x >= _y, "ERR_UNDERFLOW");
return _x - _y;
}
/**
* @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
*
* @param _x factor 1
* @param _y factor 2
*
* @return product
*/
function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
// gas optimization
if (_x == 0)
return 0;
uint256 z = _x * _y;
require(z / _x == _y, "ERR_OVERFLOW");
return z;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*
* @param _x dividend
* @param _y divisor
*
* @return quotient
*/
function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_y > 0, "ERR_DIVIDE_BY_ZERO");
uint256 c = _x / _y;
return c;
}
}
// File: solidity/contracts/converter/BancorFormula.sol
pragma solidity 0.4.26;
contract BancorFormula is IBancorFormula {
using SafeMath for uint256;
uint16 public constant version = 8;
uint256 private constant ONE = 1;
uint32 private constant MAX_WEIGHT = 1000000;
uint8 private constant MIN_PRECISION = 32;
uint8 private constant MAX_PRECISION = 127;
/**
* Auto-generated via 'PrintIntScalingFactors.py'
*/
uint256 private constant FIXED_1 = 0x080000000000000000000000000000000;
uint256 private constant FIXED_2 = 0x100000000000000000000000000000000;
uint256 private constant MAX_NUM = 0x200000000000000000000000000000000;
/**
* Auto-generated via 'PrintLn2ScalingFactors.py'
*/
uint256 private constant LN2_NUMERATOR = 0x3f80fe03f80fe03f80fe03f80fe03f8;
uint256 private constant LN2_DENOMINATOR = 0x5b9de1d10bf4103d647b0955897ba80;
/**
* Auto-generated via 'PrintFunctionOptimalLog.py' and 'PrintFunctionOptimalExp.py'
*/
uint256 private constant OPT_LOG_MAX_VAL = 0x15bf0a8b1457695355fb8ac404e7a79e3;
uint256 private constant OPT_EXP_MAX_VAL = 0x800000000000000000000000000000000;
/**
* Auto-generated via 'PrintLambertFactors.py'
*/
uint256 private constant LAMBERT_CONV_RADIUS = 0x002f16ac6c59de6f8d5d6f63c1482a7c86;
uint256 private constant LAMBERT_POS2_SAMPLE = 0x0003060c183060c183060c183060c18306;
uint256 private constant LAMBERT_POS2_MAXVAL = 0x01af16ac6c59de6f8d5d6f63c1482a7c80;
uint256 private constant LAMBERT_POS3_MAXVAL = 0x6b22d43e72c326539cceeef8bb48f255ff;
/**
* Auto-generated via 'PrintWeightFactors.py'
*/
uint256 private constant MAX_UNF_WEIGHT = 0x10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea9;
/**
* Auto-generated via 'PrintMaxExpArray.py'
*/
uint256[128] private maxExpArray;
function initMaxExpArray() private {
// maxExpArray[ 0] = 0x6bffffffffffffffffffffffffffffffff;
// maxExpArray[ 1] = 0x67ffffffffffffffffffffffffffffffff;
// maxExpArray[ 2] = 0x637fffffffffffffffffffffffffffffff;
// maxExpArray[ 3] = 0x5f6fffffffffffffffffffffffffffffff;
// maxExpArray[ 4] = 0x5b77ffffffffffffffffffffffffffffff;
// maxExpArray[ 5] = 0x57b3ffffffffffffffffffffffffffffff;
// maxExpArray[ 6] = 0x5419ffffffffffffffffffffffffffffff;
// maxExpArray[ 7] = 0x50a2ffffffffffffffffffffffffffffff;
// maxExpArray[ 8] = 0x4d517fffffffffffffffffffffffffffff;
// maxExpArray[ 9] = 0x4a233fffffffffffffffffffffffffffff;
// maxExpArray[ 10] = 0x47165fffffffffffffffffffffffffffff;
// maxExpArray[ 11] = 0x4429afffffffffffffffffffffffffffff;
// maxExpArray[ 12] = 0x415bc7ffffffffffffffffffffffffffff;
// maxExpArray[ 13] = 0x3eab73ffffffffffffffffffffffffffff;
// maxExpArray[ 14] = 0x3c1771ffffffffffffffffffffffffffff;
// maxExpArray[ 15] = 0x399e96ffffffffffffffffffffffffffff;
// maxExpArray[ 16] = 0x373fc47fffffffffffffffffffffffffff;
// maxExpArray[ 17] = 0x34f9e8ffffffffffffffffffffffffffff;
// maxExpArray[ 18] = 0x32cbfd5fffffffffffffffffffffffffff;
// maxExpArray[ 19] = 0x30b5057fffffffffffffffffffffffffff;
// maxExpArray[ 20] = 0x2eb40f9fffffffffffffffffffffffffff;
// maxExpArray[ 21] = 0x2cc8340fffffffffffffffffffffffffff;
// maxExpArray[ 22] = 0x2af09481ffffffffffffffffffffffffff;
// maxExpArray[ 23] = 0x292c5bddffffffffffffffffffffffffff;
// maxExpArray[ 24] = 0x277abdcdffffffffffffffffffffffffff;
// maxExpArray[ 25] = 0x25daf6657fffffffffffffffffffffffff;
// maxExpArray[ 26] = 0x244c49c65fffffffffffffffffffffffff;
// maxExpArray[ 27] = 0x22ce03cd5fffffffffffffffffffffffff;
// maxExpArray[ 28] = 0x215f77c047ffffffffffffffffffffffff;
// maxExpArray[ 29] = 0x1fffffffffffffffffffffffffffffffff;
// maxExpArray[ 30] = 0x1eaefdbdabffffffffffffffffffffffff;
// maxExpArray[ 31] = 0x1d6bd8b2ebffffffffffffffffffffffff;
maxExpArray[ 32] = 0x1c35fedd14ffffffffffffffffffffffff;
maxExpArray[ 33] = 0x1b0ce43b323fffffffffffffffffffffff;
maxExpArray[ 34] = 0x19f0028ec1ffffffffffffffffffffffff;
maxExpArray[ 35] = 0x18ded91f0e7fffffffffffffffffffffff;
maxExpArray[ 36] = 0x17d8ec7f0417ffffffffffffffffffffff;
maxExpArray[ 37] = 0x16ddc6556cdbffffffffffffffffffffff;
maxExpArray[ 38] = 0x15ecf52776a1ffffffffffffffffffffff;
maxExpArray[ 39] = 0x15060c256cb2ffffffffffffffffffffff;
maxExpArray[ 40] = 0x1428a2f98d72ffffffffffffffffffffff;
maxExpArray[ 41] = 0x13545598e5c23fffffffffffffffffffff;
maxExpArray[ 42] = 0x1288c4161ce1dfffffffffffffffffffff;
maxExpArray[ 43] = 0x11c592761c666fffffffffffffffffffff;
maxExpArray[ 44] = 0x110a688680a757ffffffffffffffffffff;
maxExpArray[ 45] = 0x1056f1b5bedf77ffffffffffffffffffff;
maxExpArray[ 46] = 0x0faadceceeff8bffffffffffffffffffff;
maxExpArray[ 47] = 0x0f05dc6b27edadffffffffffffffffffff;
maxExpArray[ 48] = 0x0e67a5a25da4107fffffffffffffffffff;
maxExpArray[ 49] = 0x0dcff115b14eedffffffffffffffffffff;
maxExpArray[ 50] = 0x0d3e7a392431239fffffffffffffffffff;
maxExpArray[ 51] = 0x0cb2ff529eb71e4fffffffffffffffffff;
maxExpArray[ 52] = 0x0c2d415c3db974afffffffffffffffffff;
maxExpArray[ 53] = 0x0bad03e7d883f69bffffffffffffffffff;
maxExpArray[ 54] = 0x0b320d03b2c343d5ffffffffffffffffff;
maxExpArray[ 55] = 0x0abc25204e02828dffffffffffffffffff;
maxExpArray[ 56] = 0x0a4b16f74ee4bb207fffffffffffffffff;
maxExpArray[ 57] = 0x09deaf736ac1f569ffffffffffffffffff;
maxExpArray[ 58] = 0x0976bd9952c7aa957fffffffffffffffff;
maxExpArray[ 59] = 0x09131271922eaa606fffffffffffffffff;
maxExpArray[ 60] = 0x08b380f3558668c46fffffffffffffffff;
maxExpArray[ 61] = 0x0857ddf0117efa215bffffffffffffffff;
maxExpArray[ 62] = 0x07ffffffffffffffffffffffffffffffff;
maxExpArray[ 63] = 0x07abbf6f6abb9d087fffffffffffffffff;
maxExpArray[ 64] = 0x075af62cbac95f7dfa7fffffffffffffff;
maxExpArray[ 65] = 0x070d7fb7452e187ac13fffffffffffffff;
maxExpArray[ 66] = 0x06c3390ecc8af379295fffffffffffffff;
maxExpArray[ 67] = 0x067c00a3b07ffc01fd6fffffffffffffff;
maxExpArray[ 68] = 0x0637b647c39cbb9d3d27ffffffffffffff;
maxExpArray[ 69] = 0x05f63b1fc104dbd39587ffffffffffffff;
maxExpArray[ 70] = 0x05b771955b36e12f7235ffffffffffffff;
maxExpArray[ 71] = 0x057b3d49dda84556d6f6ffffffffffffff;
maxExpArray[ 72] = 0x054183095b2c8ececf30ffffffffffffff;
maxExpArray[ 73] = 0x050a28be635ca2b888f77fffffffffffff;
maxExpArray[ 74] = 0x04d5156639708c9db33c3fffffffffffff;
maxExpArray[ 75] = 0x04a23105873875bd52dfdfffffffffffff;
maxExpArray[ 76] = 0x0471649d87199aa990756fffffffffffff;
maxExpArray[ 77] = 0x04429a21a029d4c1457cfbffffffffffff;
maxExpArray[ 78] = 0x0415bc6d6fb7dd71af2cb3ffffffffffff;
maxExpArray[ 79] = 0x03eab73b3bbfe282243ce1ffffffffffff;
maxExpArray[ 80] = 0x03c1771ac9fb6b4c18e229ffffffffffff;
maxExpArray[ 81] = 0x0399e96897690418f785257fffffffffff;
maxExpArray[ 82] = 0x0373fc456c53bb779bf0ea9fffffffffff;
maxExpArray[ 83] = 0x034f9e8e490c48e67e6ab8bfffffffffff;
maxExpArray[ 84] = 0x032cbfd4a7adc790560b3337ffffffffff;
maxExpArray[ 85] = 0x030b50570f6e5d2acca94613ffffffffff;
maxExpArray[ 86] = 0x02eb40f9f620fda6b56c2861ffffffffff;
maxExpArray[ 87] = 0x02cc8340ecb0d0f520a6af58ffffffffff;
maxExpArray[ 88] = 0x02af09481380a0a35cf1ba02ffffffffff;
maxExpArray[ 89] = 0x0292c5bdd3b92ec810287b1b3fffffffff;
maxExpArray[ 90] = 0x0277abdcdab07d5a77ac6d6b9fffffffff;
maxExpArray[ 91] = 0x025daf6654b1eaa55fd64df5efffffffff;
maxExpArray[ 92] = 0x0244c49c648baa98192dce88b7ffffffff;
maxExpArray[ 93] = 0x022ce03cd5619a311b2471268bffffffff;
maxExpArray[ 94] = 0x0215f77c045fbe885654a44a0fffffffff;
maxExpArray[ 95] = 0x01ffffffffffffffffffffffffffffffff;
maxExpArray[ 96] = 0x01eaefdbdaaee7421fc4d3ede5ffffffff;
maxExpArray[ 97] = 0x01d6bd8b2eb257df7e8ca57b09bfffffff;
maxExpArray[ 98] = 0x01c35fedd14b861eb0443f7f133fffffff;
maxExpArray[ 99] = 0x01b0ce43b322bcde4a56e8ada5afffffff;
maxExpArray[100] = 0x019f0028ec1fff007f5a195a39dfffffff;
maxExpArray[101] = 0x018ded91f0e72ee74f49b15ba527ffffff;
maxExpArray[102] = 0x017d8ec7f04136f4e5615fd41a63ffffff;
maxExpArray[103] = 0x016ddc6556cdb84bdc8d12d22e6fffffff;
maxExpArray[104] = 0x015ecf52776a1155b5bd8395814f7fffff;
maxExpArray[105] = 0x015060c256cb23b3b3cc3754cf40ffffff;
maxExpArray[106] = 0x01428a2f98d728ae223ddab715be3fffff;
maxExpArray[107] = 0x013545598e5c23276ccf0ede68034fffff;
maxExpArray[108] = 0x01288c4161ce1d6f54b7f61081194fffff;
maxExpArray[109] = 0x011c592761c666aa641d5a01a40f17ffff;
maxExpArray[110] = 0x0110a688680a7530515f3e6e6cfdcdffff;
maxExpArray[111] = 0x01056f1b5bedf75c6bcb2ce8aed428ffff;
maxExpArray[112] = 0x00faadceceeff8a0890f3875f008277fff;
maxExpArray[113] = 0x00f05dc6b27edad306388a600f6ba0bfff;
maxExpArray[114] = 0x00e67a5a25da41063de1495d5b18cdbfff;
maxExpArray[115] = 0x00dcff115b14eedde6fc3aa5353f2e4fff;
maxExpArray[116] = 0x00d3e7a3924312399f9aae2e0f868f8fff;
maxExpArray[117] = 0x00cb2ff529eb71e41582cccd5a1ee26fff;
maxExpArray[118] = 0x00c2d415c3db974ab32a51840c0b67edff;
maxExpArray[119] = 0x00bad03e7d883f69ad5b0a186184e06bff;
maxExpArray[120] = 0x00b320d03b2c343d4829abd6075f0cc5ff;
maxExpArray[121] = 0x00abc25204e02828d73c6e80bcdb1a95bf;
maxExpArray[122] = 0x00a4b16f74ee4bb2040a1ec6c15fbbf2df;
maxExpArray[123] = 0x009deaf736ac1f569deb1b5ae3f36c130f;
maxExpArray[124] = 0x00976bd9952c7aa957f5937d790ef65037;
maxExpArray[125] = 0x009131271922eaa6064b73a22d0bd4f2bf;
maxExpArray[126] = 0x008b380f3558668c46c91c49a2f8e967b9;
maxExpArray[127] = 0x00857ddf0117efa215952912839f6473e6;
}
/**
* Auto-generated via 'PrintLambertArray.py'
*/
uint256[128] private lambertArray;
function initLambertArray() private {
lambertArray[ 0] = 0x60e393c68d20b1bd09deaabc0373b9c5;
lambertArray[ 1] = 0x5f8f46e4854120989ed94719fb4c2011;
lambertArray[ 2] = 0x5e479ebb9129fb1b7e72a648f992b606;
lambertArray[ 3] = 0x5d0bd23fe42dfedde2e9586be12b85fe;
lambertArray[ 4] = 0x5bdb29ddee979308ddfca81aeeb8095a;
lambertArray[ 5] = 0x5ab4fd8a260d2c7e2c0d2afcf0009dad;
lambertArray[ 6] = 0x5998b31359a55d48724c65cf09001221;
lambertArray[ 7] = 0x5885bcad2b322dfc43e8860f9c018cf5;
lambertArray[ 8] = 0x577b97aa1fe222bb452fdf111b1f0be2;
lambertArray[ 9] = 0x5679cb5e3575632e5baa27e2b949f704;
lambertArray[ 10] = 0x557fe8241b3a31c83c732f1cdff4a1c5;
lambertArray[ 11] = 0x548d868026504875d6e59bbe95fc2a6b;
lambertArray[ 12] = 0x53a2465ce347cf34d05a867c17dd3088;
lambertArray[ 13] = 0x52bdce5dcd4faed59c7f5511cf8f8acc;
lambertArray[ 14] = 0x51dfcb453c07f8da817606e7885f7c3e;
lambertArray[ 15] = 0x5107ef6b0a5a2be8f8ff15590daa3cce;
lambertArray[ 16] = 0x5035f241d6eae0cd7bacba119993de7b;
lambertArray[ 17] = 0x4f698fe90d5b53d532171e1210164c66;
lambertArray[ 18] = 0x4ea288ca297a0e6a09a0eee240e16c85;
lambertArray[ 19] = 0x4de0a13fdcf5d4213fc398ba6e3becde;
lambertArray[ 20] = 0x4d23a145eef91fec06b06140804c4808;
lambertArray[ 21] = 0x4c6b5430d4c1ee5526473db4ae0f11de;
lambertArray[ 22] = 0x4bb7886c240562eba11f4963a53b4240;
lambertArray[ 23] = 0x4b080f3f1cb491d2d521e0ea4583521e;
lambertArray[ 24] = 0x4a5cbc96a05589cb4d86be1db3168364;
lambertArray[ 25] = 0x49b566d40243517658d78c33162d6ece;
lambertArray[ 26] = 0x4911e6a02e5507a30f947383fd9a3276;
lambertArray[ 27] = 0x487216c2b31be4adc41db8a8d5cc0c88;
lambertArray[ 28] = 0x47d5d3fc4a7a1b188cd3d788b5c5e9fc;
lambertArray[ 29] = 0x473cfce4871a2c40bc4f9e1c32b955d0;
lambertArray[ 30] = 0x46a771ca578ab878485810e285e31c67;
lambertArray[ 31] = 0x4615149718aed4c258c373dc676aa72d;
lambertArray[ 32] = 0x4585c8b3f8fe489c6e1833ca47871384;
lambertArray[ 33] = 0x44f972f174e41e5efb7e9d63c29ce735;
lambertArray[ 34] = 0x446ff970ba86d8b00beb05ecebf3c4dc;
lambertArray[ 35] = 0x43e9438ec88971812d6f198b5ccaad96;
lambertArray[ 36] = 0x436539d11ff7bea657aeddb394e809ef;
lambertArray[ 37] = 0x42e3c5d3e5a913401d86f66db5d81c2c;
lambertArray[ 38] = 0x4264d2395303070ea726cbe98df62174;
lambertArray[ 39] = 0x41e84a9a593bb7194c3a6349ecae4eea;
lambertArray[ 40] = 0x416e1b785d13eba07a08f3f18876a5ab;
lambertArray[ 41] = 0x40f6322ff389d423ba9dd7e7e7b7e809;
lambertArray[ 42] = 0x40807cec8a466880ecf4184545d240a4;
lambertArray[ 43] = 0x400cea9ce88a8d3ae668e8ea0d9bf07f;
lambertArray[ 44] = 0x3f9b6ae8772d4c55091e0ed7dfea0ac1;
lambertArray[ 45] = 0x3f2bee253fd84594f54bcaafac383a13;
lambertArray[ 46] = 0x3ebe654e95208bb9210c575c081c5958;
lambertArray[ 47] = 0x3e52c1fc5665635b78ce1f05ad53c086;
lambertArray[ 48] = 0x3de8f65ac388101ddf718a6f5c1eff65;
lambertArray[ 49] = 0x3d80f522d59bd0b328ca012df4cd2d49;
lambertArray[ 50] = 0x3d1ab193129ea72b23648a161163a85a;
lambertArray[ 51] = 0x3cb61f68d32576c135b95cfb53f76d75;
lambertArray[ 52] = 0x3c5332d9f1aae851a3619e77e4cc8473;
lambertArray[ 53] = 0x3bf1e08edbe2aa109e1525f65759ef73;
lambertArray[ 54] = 0x3b921d9cff13fa2c197746a3dfc4918f;
lambertArray[ 55] = 0x3b33df818910bfc1a5aefb8f63ae2ac4;
lambertArray[ 56] = 0x3ad71c1c77e34fa32a9f184967eccbf6;
lambertArray[ 57] = 0x3a7bc9abf2c5bb53e2f7384a8a16521a;
lambertArray[ 58] = 0x3a21dec7e76369783a68a0c6385a1c57;
lambertArray[ 59] = 0x39c9525de6c9cdf7c1c157ca4a7a6ee3;
lambertArray[ 60] = 0x39721bad3dc85d1240ff0190e0adaac3;
lambertArray[ 61] = 0x391c324344d3248f0469eb28dd3d77e0;
lambertArray[ 62] = 0x38c78df7e3c796279fb4ff84394ab3da;
lambertArray[ 63] = 0x387426ea4638ae9aae08049d3554c20a;
lambertArray[ 64] = 0x3821f57dbd2763256c1a99bbd2051378;
lambertArray[ 65] = 0x37d0f256cb46a8c92ff62fbbef289698;
lambertArray[ 66] = 0x37811658591ffc7abdd1feaf3cef9b73;
lambertArray[ 67] = 0x37325aa10e9e82f7df0f380f7997154b;
lambertArray[ 68] = 0x36e4b888cfb408d873b9a80d439311c6;
lambertArray[ 69] = 0x3698299e59f4bb9de645fc9b08c64cca;
lambertArray[ 70] = 0x364ca7a5012cb603023b57dd3ebfd50d;
lambertArray[ 71] = 0x36022c928915b778ab1b06aaee7e61d4;
lambertArray[ 72] = 0x35b8b28d1a73dc27500ffe35559cc028;
lambertArray[ 73] = 0x357033e951fe250ec5eb4e60955132d7;
lambertArray[ 74] = 0x3528ab2867934e3a21b5412e4c4f8881;
lambertArray[ 75] = 0x34e212f66c55057f9676c80094a61d59;
lambertArray[ 76] = 0x349c66289e5b3c4b540c24f42fa4b9bb;
lambertArray[ 77] = 0x34579fbbd0c733a9c8d6af6b0f7d00f7;
lambertArray[ 78] = 0x3413bad2e712288b924b5882b5b369bf;
lambertArray[ 79] = 0x33d0b2b56286510ef730e213f71f12e9;
lambertArray[ 80] = 0x338e82ce00e2496262c64457535ba1a1;
lambertArray[ 81] = 0x334d26a96b373bb7c2f8ea1827f27a92;
lambertArray[ 82] = 0x330c99f4f4211469e00b3e18c31475ea;
lambertArray[ 83] = 0x32ccd87d6486094999c7d5e6f33237d8;
lambertArray[ 84] = 0x328dde2dd617b6665a2e8556f250c1af;
lambertArray[ 85] = 0x324fa70e9adc270f8262755af5a99af9;
lambertArray[ 86] = 0x32122f443110611ca51040f41fa6e1e3;
lambertArray[ 87] = 0x31d5730e42c0831482f0f1485c4263d8;
lambertArray[ 88] = 0x31996ec6b07b4a83421b5ebc4ab4e1f1;
lambertArray[ 89] = 0x315e1ee0a68ff46bb43ec2b85032e876;
lambertArray[ 90] = 0x31237fe7bc4deacf6775b9efa1a145f8;
lambertArray[ 91] = 0x30e98e7f1cc5a356e44627a6972ea2ff;
lambertArray[ 92] = 0x30b04760b8917ec74205a3002650ec05;
lambertArray[ 93] = 0x3077a75c803468e9132ce0cf3224241d;
lambertArray[ 94] = 0x303fab57a6a275c36f19cda9bace667a;
lambertArray[ 95] = 0x3008504beb8dcbd2cf3bc1f6d5a064f0;
lambertArray[ 96] = 0x2fd19346ed17dac61219ce0c2c5ac4b0;
lambertArray[ 97] = 0x2f9b7169808c324b5852fd3d54ba9714;
lambertArray[ 98] = 0x2f65e7e711cf4b064eea9c08cbdad574;
lambertArray[ 99] = 0x2f30f405093042ddff8a251b6bf6d103;
lambertArray[100] = 0x2efc931a3750f2e8bfe323edfe037574;
lambertArray[101] = 0x2ec8c28e46dbe56d98685278339400cb;
lambertArray[102] = 0x2e957fd933c3926d8a599b602379b851;
lambertArray[103] = 0x2e62c882c7c9ed4473412702f08ba0e5;
lambertArray[104] = 0x2e309a221c12ba361e3ed695167feee2;
lambertArray[105] = 0x2dfef25d1f865ae18dd07cfea4bcea10;
lambertArray[106] = 0x2dcdcee821cdc80decc02c44344aeb31;
lambertArray[107] = 0x2d9d2d8562b34944d0b201bb87260c83;
lambertArray[108] = 0x2d6d0c04a5b62a2c42636308669b729a;
lambertArray[109] = 0x2d3d6842c9a235517fc5a0332691528f;
lambertArray[110] = 0x2d0e402963fe1ea2834abc408c437c10;
lambertArray[111] = 0x2cdf91ae602647908aff975e4d6a2a8c;
lambertArray[112] = 0x2cb15ad3a1eb65f6d74a75da09a1b6c5;
lambertArray[113] = 0x2c8399a6ab8e9774d6fcff373d210727;
lambertArray[114] = 0x2c564c4046f64edba6883ca06bbc4535;
lambertArray[115] = 0x2c2970c431f952641e05cb493e23eed3;
lambertArray[116] = 0x2bfd0560cd9eb14563bc7c0732856c18;
lambertArray[117] = 0x2bd1084ed0332f7ff4150f9d0ef41a2c;
lambertArray[118] = 0x2ba577d0fa1628b76d040b12a82492fb;
lambertArray[119] = 0x2b7a5233cd21581e855e89dc2f1e8a92;
lambertArray[120] = 0x2b4f95cd46904d05d72bdcde337d9cc7;
lambertArray[121] = 0x2b2540fc9b4d9abba3faca6691914675;
lambertArray[122] = 0x2afb5229f68d0830d8be8adb0a0db70f;
lambertArray[123] = 0x2ad1c7c63a9b294c5bc73a3ba3ab7a2b;
lambertArray[124] = 0x2aa8a04ac3cbe1ee1c9c86361465dbb8;
lambertArray[125] = 0x2a7fda392d725a44a2c8aeb9ab35430d;
lambertArray[126] = 0x2a57741b18cde618717792b4faa216db;
lambertArray[127] = 0x2a2f6c81f5d84dd950a35626d6d5503a;
}
/**
* @dev should be executed after construction (too large for the constructor)
*/
function init() public {
initMaxExpArray();
initLambertArray();
}
/**
* @dev given a token supply, reserve balance, weight and a deposit amount (in the reserve token),
* calculates the target amount for a given conversion (in the main token)
*
* Formula:
* return = _supply * ((1 + _amount / _reserveBalance) ^ (_reserveWeight / 1000000) - 1)
*
* @param _supply smart token supply
* @param _reserveBalance reserve balance
* @param _reserveWeight reserve weight, represented in ppm (1-1000000)
* @param _amount amount of reserve tokens to get the target amount for
*
* @return smart token amount
*/
function purchaseTargetAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256)
{
// validate input
require(_supply > 0, "ERR_INVALID_SUPPLY");
require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
require(_reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT, "ERR_INVALID_RESERVE_WEIGHT");
// special case for 0 deposit amount
if (_amount == 0)
return 0;
// special case if the weight = 100%
if (_reserveWeight == MAX_WEIGHT)
return _supply.mul(_amount) / _reserveBalance;
uint256 result;
uint8 precision;
uint256 baseN = _amount.add(_reserveBalance);
(result, precision) = power(baseN, _reserveBalance, _reserveWeight, MAX_WEIGHT);
uint256 temp = _supply.mul(result) >> precision;
return temp - _supply;
}
/**
* @dev given a token supply, reserve balance, weight and a sell amount (in the main token),
* calculates the target amount for a given conversion (in the reserve token)
*
* Formula:
* return = _reserveBalance * (1 - (1 - _amount / _supply) ^ (1000000 / _reserveWeight))
*
* @param _supply smart token supply
* @param _reserveBalance reserve balance
* @param _reserveWeight reserve weight, represented in ppm (1-1000000)
* @param _amount amount of smart tokens to get the target amount for
*
* @return reserve token amount
*/
function saleTargetAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256)
{
// validate input
require(_supply > 0, "ERR_INVALID_SUPPLY");
require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
require(_reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT, "ERR_INVALID_RESERVE_WEIGHT");
require(_amount <= _supply, "ERR_INVALID_AMOUNT");
// special case for 0 sell amount
if (_amount == 0)
return 0;
// special case for selling the entire supply
if (_amount == _supply)
return _reserveBalance;
// special case if the weight = 100%
if (_reserveWeight == MAX_WEIGHT)
return _reserveBalance.mul(_amount) / _supply;
uint256 result;
uint8 precision;
uint256 baseD = _supply - _amount;
(result, precision) = power(_supply, baseD, MAX_WEIGHT, _reserveWeight);
uint256 temp1 = _reserveBalance.mul(result);
uint256 temp2 = _reserveBalance << precision;
return (temp1 - temp2) / result;
}
/**
* @dev given two reserve balances/weights and a sell amount (in the first reserve token),
* calculates the target amount for a conversion from the source reserve token to the target reserve token
*
* Formula:
* return = _targetReserveBalance * (1 - (_sourceReserveBalance / (_sourceReserveBalance + _amount)) ^ (_sourceReserveWeight / _targetReserveWeight))
*
* @param _sourceReserveBalance source reserve balance
* @param _sourceReserveWeight source reserve weight, represented in ppm (1-1000000)
* @param _targetReserveBalance target reserve balance
* @param _targetReserveWeight target reserve weight, represented in ppm (1-1000000)
* @param _amount source reserve amount
*
* @return target reserve amount
*/
function crossReserveTargetAmount(uint256 _sourceReserveBalance,
uint32 _sourceReserveWeight,
uint256 _targetReserveBalance,
uint32 _targetReserveWeight,
uint256 _amount)
public view returns (uint256)
{
// validate input
require(_sourceReserveBalance > 0 && _targetReserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
require(_sourceReserveWeight > 0 && _sourceReserveWeight <= MAX_WEIGHT &&
_targetReserveWeight > 0 && _targetReserveWeight <= MAX_WEIGHT, "ERR_INVALID_RESERVE_WEIGHT");
// special case for equal weights
if (_sourceReserveWeight == _targetReserveWeight)
return _targetReserveBalance.mul(_amount) / _sourceReserveBalance.add(_amount);
uint256 result;
uint8 precision;
uint256 baseN = _sourceReserveBalance.add(_amount);
(result, precision) = power(baseN, _sourceReserveBalance, _sourceReserveWeight, _targetReserveWeight);
uint256 temp1 = _targetReserveBalance.mul(result);
uint256 temp2 = _targetReserveBalance << precision;
return (temp1 - temp2) / result;
}
/**
* @dev given a smart token supply, reserve balance, reserve ratio and an amount of requested smart tokens,
* calculates the amount of reserve tokens required for purchasing the given amount of smart tokens
*
* Formula:
* return = _reserveBalance * (((_supply + _amount) / _supply) ^ (MAX_WEIGHT / _reserveRatio) - 1)
*
* @param _supply smart token supply
* @param _reserveBalance reserve balance
* @param _reserveRatio reserve ratio, represented in ppm (2-2000000)
* @param _amount requested amount of smart tokens
*
* @return reserve token amount
*/
function fundCost(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256)
{
// validate input
require(_supply > 0, "ERR_INVALID_SUPPLY");
require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
require(_reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2, "ERR_INVALID_RESERVE_RATIO");
// special case for 0 amount
if (_amount == 0)
return 0;
// special case if the reserve ratio = 100%
if (_reserveRatio == MAX_WEIGHT)
return (_amount.mul(_reserveBalance) - 1) / _supply + 1;
uint256 result;
uint8 precision;
uint256 baseN = _supply.add(_amount);
(result, precision) = power(baseN, _supply, MAX_WEIGHT, _reserveRatio);
uint256 temp = ((_reserveBalance.mul(result) - 1) >> precision) + 1;
return temp - _reserveBalance;
}
/**
* @dev given a smart token supply, reserve balance, reserve ratio and an amount of reserve tokens to fund with,
* calculates the amount of smart tokens received for purchasing with the given amount of reserve tokens
*
* Formula:
* return = _supply * ((_amount / _reserveBalance + 1) ^ (_reserveRatio / MAX_WEIGHT) - 1)
*
* @param _supply smart token supply
* @param _reserveBalance reserve balance
* @param _reserveRatio reserve ratio, represented in ppm (2-2000000)
* @param _amount amount of reserve tokens to fund with
*
* @return smart token amount
*/
function fundSupplyAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256)
{
// validate input
require(_supply > 0, "ERR_INVALID_SUPPLY");
require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
require(_reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2, "ERR_INVALID_RESERVE_RATIO");
// special case for 0 amount
if (_amount == 0)
return 0;
// special case if the reserve ratio = 100%
if (_reserveRatio == MAX_WEIGHT)
return _amount.mul(_supply) / _reserveBalance;
uint256 result;
uint8 precision;
uint256 baseN = _reserveBalance.add(_amount);
(result, precision) = power(baseN, _reserveBalance, _reserveRatio, MAX_WEIGHT);
uint256 temp = _supply.mul(result) >> precision;
return temp - _supply;
}
/**
* @dev given a smart token supply, reserve balance, reserve ratio and an amount of smart tokens to liquidate,
* calculates the amount of reserve tokens received for selling the given amount of smart tokens
*
* Formula:
* return = _reserveBalance * (1 - ((_supply - _amount) / _supply) ^ (MAX_WEIGHT / _reserveRatio))
*
* @param _supply smart token supply
* @param _reserveBalance reserve balance
* @param _reserveRatio reserve ratio, represented in ppm (2-2000000)
* @param _amount amount of smart tokens to liquidate
*
* @return reserve token amount
*/
function liquidateReserveAmount(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256)
{
// validate input
require(_supply > 0, "ERR_INVALID_SUPPLY");
require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
require(_reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2, "ERR_INVALID_RESERVE_RATIO");
require(_amount <= _supply, "ERR_INVALID_AMOUNT");
// special case for 0 amount
if (_amount == 0)
return 0;
// special case for liquidating the entire supply
if (_amount == _supply)
return _reserveBalance;
// special case if the reserve ratio = 100%
if (_reserveRatio == MAX_WEIGHT)
return _amount.mul(_reserveBalance) / _supply;
uint256 result;
uint8 precision;
uint256 baseD = _supply - _amount;
(result, precision) = power(_supply, baseD, MAX_WEIGHT, _reserveRatio);
uint256 temp1 = _reserveBalance.mul(result);
uint256 temp2 = _reserveBalance << precision;
return (temp1 - temp2) / result;
}
/**
* @dev The arbitrage incentive is to convert to the point where the on-chain price is equal to the off-chain price.
* We want this operation to also impact the primary reserve balance becoming equal to the primary reserve staked balance.
* In other words, we want the arbitrager to convert the difference between the reserve balance and the reserve staked balance.
*
* Formula input:
* - let t denote the primary reserve token staked balance
* - let s denote the primary reserve token balance
* - let r denote the secondary reserve token balance
* - let q denote the numerator of the rate between the tokens
* - let p denote the denominator of the rate between the tokens
* Where p primary tokens are equal to q secondary tokens
*
* Formula output:
* - compute x = W(t / r * q / p * log(s / t)) / log(s / t)
* - return x / (1 + x) as the weight of the primary reserve token
* - return 1 / (1 + x) as the weight of the secondary reserve token
* Where W is the Lambert W Function
*
* If the rate-provider provides the rates for a common unit, for example:
* - P = 2 ==> 2 primary reserve tokens = 1 ether
* - Q = 3 ==> 3 secondary reserve tokens = 1 ether
* Then you can simply use p = P and q = Q
*
* If the rate-provider provides the rates for a single unit, for example:
* - P = 2 ==> 1 primary reserve token = 2 ethers
* - Q = 3 ==> 1 secondary reserve token = 3 ethers
* Then you can simply use p = Q and q = P
*
* @param _primaryReserveStakedBalance the primary reserve token staked balance
* @param _primaryReserveBalance the primary reserve token balance
* @param _secondaryReserveBalance the secondary reserve token balance
* @param _reserveRateNumerator the numerator of the rate between the tokens
* @param _reserveRateDenominator the denominator of the rate between the tokens
*
* Note that `numerator / denominator` should represent the amount of secondary tokens equal to one primary token
*
* @return the weight of the primary reserve token and the weight of the secondary reserve token, both in ppm (0-1000000)
*/
function balancedWeights(uint256 _primaryReserveStakedBalance,
uint256 _primaryReserveBalance,
uint256 _secondaryReserveBalance,
uint256 _reserveRateNumerator,
uint256 _reserveRateDenominator)
public view returns (uint32, uint32)
{
if (_primaryReserveStakedBalance == _primaryReserveBalance)
require(_primaryReserveStakedBalance > 0 || _secondaryReserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
else
require(_primaryReserveStakedBalance > 0 && _primaryReserveBalance > 0 && _secondaryReserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
require(_reserveRateNumerator > 0 && _reserveRateDenominator > 0, "ERR_INVALID_RESERVE_RATE");
uint256 tq = _primaryReserveStakedBalance.mul(_reserveRateNumerator);
uint256 rp = _secondaryReserveBalance.mul(_reserveRateDenominator);
if (_primaryReserveStakedBalance < _primaryReserveBalance)
return balancedWeightsByStake(_primaryReserveBalance, _primaryReserveStakedBalance, tq, rp, true);
if (_primaryReserveStakedBalance > _primaryReserveBalance)
return balancedWeightsByStake(_primaryReserveStakedBalance, _primaryReserveBalance, tq, rp, false);
return normalizedWeights(tq, rp);
}
/**
* @dev General Description:
* Determine a value of precision.
* Calculate an integer approximation of (_baseN / _baseD) ^ (_expN / _expD) * 2 ^ precision.
* Return the result along with the precision used.
*
* Detailed Description:
* Instead of calculating "base ^ exp", we calculate "e ^ (log(base) * exp)".
* The value of "log(base)" is represented with an integer slightly smaller than "log(base) * 2 ^ precision".
* The larger "precision" is, the more accurately this value represents the real value.
* However, the larger "precision" is, the more bits are required in order to store this value.
* And the exponentiation function, which takes "x" and calculates "e ^ x", is limited to a maximum exponent (maximum value of "x").
* This maximum exponent depends on the "precision" used, and it is given by "maxExpArray[precision] >> (MAX_PRECISION - precision)".
* Hence we need to determine the highest precision which can be used for the given input, before calling the exponentiation function.
* This allows us to compute "base ^ exp" with maximum accuracy and without exceeding 256 bits in any of the intermediate computations.
* This functions assumes that "_expN < 2 ^ 256 / log(MAX_NUM - 1)", otherwise the multiplication should be replaced with a "safeMul".
* Since we rely on unsigned-integer arithmetic and "base < 1" ==> "log(base) < 0", this function does not support "_baseN < _baseD".
*/
function power(uint256 _baseN, uint256 _baseD, uint32 _expN, uint32 _expD) internal view returns (uint256, uint8) {
require(_baseN < MAX_NUM);
uint256 baseLog;
uint256 base = _baseN * FIXED_1 / _baseD;
if (base < OPT_LOG_MAX_VAL) {
baseLog = optimalLog(base);
}
else {
baseLog = generalLog(base);
}
uint256 baseLogTimesExp = baseLog * _expN / _expD;
if (baseLogTimesExp < OPT_EXP_MAX_VAL) {
return (optimalExp(baseLogTimesExp), MAX_PRECISION);
}
else {
uint8 precision = findPositionInMaxExpArray(baseLogTimesExp);
return (generalExp(baseLogTimesExp >> (MAX_PRECISION - precision), precision), precision);
}
}
/**
* @dev computes log(x / FIXED_1) * FIXED_1.
* This functions assumes that "x >= FIXED_1", because the output would be negative otherwise.
*/
function generalLog(uint256 x) internal pure returns (uint256) {
uint256 res = 0;
// If x >= 2, then we compute the integer part of log2(x), which is larger than 0.
if (x >= FIXED_2) {
uint8 count = floorLog2(x / FIXED_1);
x >>= count; // now x < 2
res = count * FIXED_1;
}
// If x > 1, then we compute the fraction part of log2(x), which is larger than 0.
if (x > FIXED_1) {
for (uint8 i = MAX_PRECISION; i > 0; --i) {
x = (x * x) / FIXED_1; // now 1 < x < 4
if (x >= FIXED_2) {
x >>= 1; // now 1 < x < 2
res += ONE << (i - 1);
}
}
}
return res * LN2_NUMERATOR / LN2_DENOMINATOR;
}
/**
* @dev computes the largest integer smaller than or equal to the binary logarithm of the input.
*/
function floorLog2(uint256 _n) internal pure returns (uint8) {
uint8 res = 0;
if (_n < 256) {
// At most 8 iterations
while (_n > 1) {
_n >>= 1;
res += 1;
}
}
else {
// Exactly 8 iterations
for (uint8 s = 128; s > 0; s >>= 1) {
if (_n >= (ONE << s)) {
_n >>= s;
res |= s;
}
}
}
return res;
}
/**
* @dev the global "maxExpArray" is sorted in descending order, and therefore the following statements are equivalent:
* - This function finds the position of [the smallest value in "maxExpArray" larger than or equal to "x"]
* - This function finds the highest position of [a value in "maxExpArray" larger than or equal to "x"]
*/
function findPositionInMaxExpArray(uint256 _x) internal view returns (uint8) {
uint8 lo = MIN_PRECISION;
uint8 hi = MAX_PRECISION;
while (lo + 1 < hi) {
uint8 mid = (lo + hi) / 2;
if (maxExpArray[mid] >= _x)
lo = mid;
else
hi = mid;
}
if (maxExpArray[hi] >= _x)
return hi;
if (maxExpArray[lo] >= _x)
return lo;
require(false);
}
/**
* @dev this function can be auto-generated by the script 'PrintFunctionGeneralExp.py'.
* it approximates "e ^ x" via maclaurin summation: "(x^0)/0! + (x^1)/1! + ... + (x^n)/n!".
* it returns "e ^ (x / 2 ^ precision) * 2 ^ precision", that is, the result is upshifted for accuracy.
* the global "maxExpArray" maps each "precision" to "((maximumExponent + 1) << (MAX_PRECISION - precision)) - 1".
* the maximum permitted value for "x" is therefore given by "maxExpArray[precision] >> (MAX_PRECISION - precision)".
*/
function generalExp(uint256 _x, uint8 _precision) internal pure returns (uint256) {
uint256 xi = _x;
uint256 res = 0;
xi = (xi * _x) >> _precision; res += xi * 0x3442c4e6074a82f1797f72ac0000000; // add x^02 * (33! / 02!)
xi = (xi * _x) >> _precision; res += xi * 0x116b96f757c380fb287fd0e40000000; // add x^03 * (33! / 03!)
xi = (xi * _x) >> _precision; res += xi * 0x045ae5bdd5f0e03eca1ff4390000000; // add x^04 * (33! / 04!)
xi = (xi * _x) >> _precision; res += xi * 0x00defabf91302cd95b9ffda50000000; // add x^05 * (33! / 05!)
xi = (xi * _x) >> _precision; res += xi * 0x002529ca9832b22439efff9b8000000; // add x^06 * (33! / 06!)
xi = (xi * _x) >> _precision; res += xi * 0x00054f1cf12bd04e516b6da88000000; // add x^07 * (33! / 07!)
xi = (xi * _x) >> _precision; res += xi * 0x0000a9e39e257a09ca2d6db51000000; // add x^08 * (33! / 08!)
xi = (xi * _x) >> _precision; res += xi * 0x000012e066e7b839fa050c309000000; // add x^09 * (33! / 09!)
xi = (xi * _x) >> _precision; res += xi * 0x000001e33d7d926c329a1ad1a800000; // add x^10 * (33! / 10!)
xi = (xi * _x) >> _precision; res += xi * 0x0000002bee513bdb4a6b19b5f800000; // add x^11 * (33! / 11!)
xi = (xi * _x) >> _precision; res += xi * 0x00000003a9316fa79b88eccf2a00000; // add x^12 * (33! / 12!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000048177ebe1fa812375200000; // add x^13 * (33! / 13!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000005263fe90242dcbacf00000; // add x^14 * (33! / 14!)
xi = (xi * _x) >> _precision; res += xi * 0x000000000057e22099c030d94100000; // add x^15 * (33! / 15!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000000057e22099c030d9410000; // add x^16 * (33! / 16!)
xi = (xi * _x) >> _precision; res += xi * 0x00000000000052b6b54569976310000; // add x^17 * (33! / 17!)
xi = (xi * _x) >> _precision; res += xi * 0x00000000000004985f67696bf748000; // add x^18 * (33! / 18!)
xi = (xi * _x) >> _precision; res += xi * 0x000000000000003dea12ea99e498000; // add x^19 * (33! / 19!)
xi = (xi * _x) >> _precision; res += xi * 0x00000000000000031880f2214b6e000; // add x^20 * (33! / 20!)
xi = (xi * _x) >> _precision; res += xi * 0x000000000000000025bcff56eb36000; // add x^21 * (33! / 21!)
xi = (xi * _x) >> _precision; res += xi * 0x000000000000000001b722e10ab1000; // add x^22 * (33! / 22!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000001317c70077000; // add x^23 * (33! / 23!)
xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000cba84aafa00; // add x^24 * (33! / 24!)
xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000082573a0a00; // add x^25 * (33! / 25!)
xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000005035ad900; // add x^26 * (33! / 26!)
xi = (xi * _x) >> _precision; res += xi * 0x000000000000000000000002f881b00; // add x^27 * (33! / 27!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000001b29340; // add x^28 * (33! / 28!)
xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000000000efc40; // add x^29 * (33! / 29!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000007fe0; // add x^30 * (33! / 30!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000420; // add x^31 * (33! / 31!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000021; // add x^32 * (33! / 32!)
xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000001; // add x^33 * (33! / 33!)
return res / 0x688589cc0e9505e2f2fee5580000000 + _x + (ONE << _precision); // divide by 33! and then add x^1 / 1! + x^0 / 0!
}
/**
* @dev computes log(x / FIXED_1) * FIXED_1
* Input range: FIXED_1 <= x <= OPT_LOG_MAX_VAL - 1
* Auto-generated via 'PrintFunctionOptimalLog.py'
* Detailed description:
* - Rewrite the input as a product of natural exponents and a single residual r, such that 1 < r < 2
* - The natural logarithm of each (pre-calculated) exponent is the degree of the exponent
* - The natural logarithm of r is calculated via Taylor series for log(1 + x), where x = r - 1
* - The natural logarithm of the input is calculated by summing up the intermediate results above
* - For example: log(250) = log(e^4 * e^1 * e^0.5 * 1.021692859) = 4 + 1 + 0.5 + log(1 + 0.021692859)
*/
function optimalLog(uint256 x) internal pure returns (uint256) {
uint256 res = 0;
uint256 y;
uint256 z;
uint256 w;
if (x >= 0xd3094c70f034de4b96ff7d5b6f99fcd8) {res += 0x40000000000000000000000000000000; x = x * FIXED_1 / 0xd3094c70f034de4b96ff7d5b6f99fcd8;} // add 1 / 2^1
if (x >= 0xa45af1e1f40c333b3de1db4dd55f29a7) {res += 0x20000000000000000000000000000000; x = x * FIXED_1 / 0xa45af1e1f40c333b3de1db4dd55f29a7;} // add 1 / 2^2
if (x >= 0x910b022db7ae67ce76b441c27035c6a1) {res += 0x10000000000000000000000000000000; x = x * FIXED_1 / 0x910b022db7ae67ce76b441c27035c6a1;} // add 1 / 2^3
if (x >= 0x88415abbe9a76bead8d00cf112e4d4a8) {res += 0x08000000000000000000000000000000; x = x * FIXED_1 / 0x88415abbe9a76bead8d00cf112e4d4a8;} // add 1 / 2^4
if (x >= 0x84102b00893f64c705e841d5d4064bd3) {res += 0x04000000000000000000000000000000; x = x * FIXED_1 / 0x84102b00893f64c705e841d5d4064bd3;} // add 1 / 2^5
if (x >= 0x8204055aaef1c8bd5c3259f4822735a2) {res += 0x02000000000000000000000000000000; x = x * FIXED_1 / 0x8204055aaef1c8bd5c3259f4822735a2;} // add 1 / 2^6
if (x >= 0x810100ab00222d861931c15e39b44e99) {res += 0x01000000000000000000000000000000; x = x * FIXED_1 / 0x810100ab00222d861931c15e39b44e99;} // add 1 / 2^7
if (x >= 0x808040155aabbbe9451521693554f733) {res += 0x00800000000000000000000000000000; x = x * FIXED_1 / 0x808040155aabbbe9451521693554f733;} // add 1 / 2^8
z = y = x - FIXED_1;
w = y * y / FIXED_1;
res += z * (0x100000000000000000000000000000000 - y) / 0x100000000000000000000000000000000; z = z * w / FIXED_1; // add y^01 / 01 - y^02 / 02
res += z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y) / 0x200000000000000000000000000000000; z = z * w / FIXED_1; // add y^03 / 03 - y^04 / 04
res += z * (0x099999999999999999999999999999999 - y) / 0x300000000000000000000000000000000; z = z * w / FIXED_1; // add y^05 / 05 - y^06 / 06
res += z * (0x092492492492492492492492492492492 - y) / 0x400000000000000000000000000000000; z = z * w / FIXED_1; // add y^07 / 07 - y^08 / 08
res += z * (0x08e38e38e38e38e38e38e38e38e38e38e - y) / 0x500000000000000000000000000000000; z = z * w / FIXED_1; // add y^09 / 09 - y^10 / 10
res += z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y) / 0x600000000000000000000000000000000; z = z * w / FIXED_1; // add y^11 / 11 - y^12 / 12
res += z * (0x089d89d89d89d89d89d89d89d89d89d89 - y) / 0x700000000000000000000000000000000; z = z * w / FIXED_1; // add y^13 / 13 - y^14 / 14
res += z * (0x088888888888888888888888888888888 - y) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16
return res;
}
/**
* @dev computes e ^ (x / FIXED_1) * FIXED_1
* input range: 0 <= x <= OPT_EXP_MAX_VAL - 1
* auto-generated via 'PrintFunctionOptimalExp.py'
* Detailed description:
* - Rewrite the input as a sum of binary exponents and a single residual r, as small as possible
* - The exponentiation of each binary exponent is given (pre-calculated)
* - The exponentiation of r is calculated via Taylor series for e^x, where x = r
* - The exponentiation of the input is calculated by multiplying the intermediate results above
* - For example: e^5.521692859 = e^(4 + 1 + 0.5 + 0.021692859) = e^4 * e^1 * e^0.5 * e^0.021692859
*/
function optimalExp(uint256 x) internal pure returns (uint256) {
uint256 res = 0;
uint256 y;
uint256 z;
z = y = x % 0x10000000000000000000000000000000; // get the input modulo 2^(-3)
z = z * y / FIXED_1; res += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!)
z = z * y / FIXED_1; res += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!)
z = z * y / FIXED_1; res += z * 0x0168244fdac78000; // add y^04 * (20! / 04!)
z = z * y / FIXED_1; res += z * 0x004807432bc18000; // add y^05 * (20! / 05!)
z = z * y / FIXED_1; res += z * 0x000c0135dca04000; // add y^06 * (20! / 06!)
z = z * y / FIXED_1; res += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!)
z = z * y / FIXED_1; res += z * 0x000036e0f639b800; // add y^08 * (20! / 08!)
z = z * y / FIXED_1; res += z * 0x00000618fee9f800; // add y^09 * (20! / 09!)
z = z * y / FIXED_1; res += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!)
z = z * y / FIXED_1; res += z * 0x0000000e30dce400; // add y^11 * (20! / 11!)
z = z * y / FIXED_1; res += z * 0x000000012ebd1300; // add y^12 * (20! / 12!)
z = z * y / FIXED_1; res += z * 0x0000000017499f00; // add y^13 * (20! / 13!)
z = z * y / FIXED_1; res += z * 0x0000000001a9d480; // add y^14 * (20! / 14!)
z = z * y / FIXED_1; res += z * 0x00000000001c6380; // add y^15 * (20! / 15!)
z = z * y / FIXED_1; res += z * 0x000000000001c638; // add y^16 * (20! / 16!)
z = z * y / FIXED_1; res += z * 0x0000000000001ab8; // add y^17 * (20! / 17!)
z = z * y / FIXED_1; res += z * 0x000000000000017c; // add y^18 * (20! / 18!)
z = z * y / FIXED_1; res += z * 0x0000000000000014; // add y^19 * (20! / 19!)
z = z * y / FIXED_1; res += z * 0x0000000000000001; // add y^20 * (20! / 20!)
res = res / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!
if ((x & 0x010000000000000000000000000000000) != 0) res = res * 0x1c3d6a24ed82218787d624d3e5eba95f9 / 0x18ebef9eac820ae8682b9793ac6d1e776; // multiply by e^2^(-3)
if ((x & 0x020000000000000000000000000000000) != 0) res = res * 0x18ebef9eac820ae8682b9793ac6d1e778 / 0x1368b2fc6f9609fe7aceb46aa619baed4; // multiply by e^2^(-2)
if ((x & 0x040000000000000000000000000000000) != 0) res = res * 0x1368b2fc6f9609fe7aceb46aa619baed5 / 0x0bc5ab1b16779be3575bd8f0520a9f21f; // multiply by e^2^(-1)
if ((x & 0x080000000000000000000000000000000) != 0) res = res * 0x0bc5ab1b16779be3575bd8f0520a9f21e / 0x0454aaa8efe072e7f6ddbab84b40a55c9; // multiply by e^2^(+0)
if ((x & 0x100000000000000000000000000000000) != 0) res = res * 0x0454aaa8efe072e7f6ddbab84b40a55c5 / 0x00960aadc109e7a3bf4578099615711ea; // multiply by e^2^(+1)
if ((x & 0x200000000000000000000000000000000) != 0) res = res * 0x00960aadc109e7a3bf4578099615711d7 / 0x0002bf84208204f5977f9a8cf01fdce3d; // multiply by e^2^(+2)
if ((x & 0x400000000000000000000000000000000) != 0) res = res * 0x0002bf84208204f5977f9a8cf01fdc307 / 0x0000003c6ab775dd0b95b4cbee7e65d11; // multiply by e^2^(+3)
return res;
}
/**
* @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
*/
function lowerStake(uint256 _x) internal view returns (uint256) {
if (_x <= LAMBERT_CONV_RADIUS)
return lambertPos1(_x);
if (_x <= LAMBERT_POS2_MAXVAL)
return lambertPos2(_x);
if (_x <= LAMBERT_POS3_MAXVAL)
return lambertPos3(_x);
require(false);
}
/**
* @dev computes W(-x / FIXED_1) / (-x / FIXED_1) * FIXED_1
*/
function higherStake(uint256 _x) internal pure returns (uint256) {
if (_x <= LAMBERT_CONV_RADIUS)
return lambertNeg1(_x);
return FIXED_1 * FIXED_1 / _x;
}
/**
* @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
* input range: 1 <= x <= 1 / e * FIXED_1
* auto-generated via 'PrintFunctionLambertPos1.py'
*/
function lambertPos1(uint256 _x) internal pure returns (uint256) {
uint256 xi = _x;
uint256 res = (FIXED_1 - _x) * 0xde1bc4d19efcac82445da75b00000000; // x^(1-1) * (34! * 1^(1-1) / 1!) - x^(2-1) * (34! * 2^(2-1) / 2!)
xi = (xi * _x) / FIXED_1; res += xi * 0x00000000014d29a73a6e7b02c3668c7b0880000000; // add x^(03-1) * (34! * 03^(03-1) / 03!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x0000000002504a0cd9a7f7215b60f9be4800000000; // sub x^(04-1) * (34! * 04^(04-1) / 04!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000000000484d0a1191c0ead267967c7a4a0000000; // add x^(05-1) * (34! * 05^(05-1) / 05!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x00000000095ec580d7e8427a4baf26a90a00000000; // sub x^(06-1) * (34! * 06^(06-1) / 06!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000000001440b0be1615a47dba6e5b3b1f10000000; // add x^(07-1) * (34! * 07^(07-1) / 07!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x000000002d207601f46a99b4112418400000000000; // sub x^(08-1) * (34! * 08^(08-1) / 08!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000000066ebaac4c37c622dd8288a7eb1b2000000; // add x^(09-1) * (34! * 09^(09-1) / 09!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x00000000ef17240135f7dbd43a1ba10cf200000000; // sub x^(10-1) * (34! * 10^(10-1) / 10!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000000233c33c676a5eb2416094a87b3657000000; // add x^(11-1) * (34! * 11^(11-1) / 11!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x0000000541cde48bc0254bed49a9f8700000000000; // sub x^(12-1) * (34! * 12^(12-1) / 12!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000000cae1fad2cdd4d4cb8d73abca0d19a400000; // add x^(13-1) * (34! * 13^(13-1) / 13!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x0000001edb2aa2f760d15c41ceedba956400000000; // sub x^(14-1) * (34! * 14^(14-1) / 14!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000004ba8d20d2dabd386c9529659841a2e200000; // add x^(15-1) * (34! * 15^(15-1) / 15!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x000000bac08546b867cdaa20000000000000000000; // sub x^(16-1) * (34! * 16^(16-1) / 16!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000001cfa8e70c03625b9db76c8ebf5bbf24820000; // add x^(17-1) * (34! * 17^(17-1) / 17!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x000004851d99f82060df265f3309b26f8200000000; // sub x^(18-1) * (34! * 18^(18-1) / 18!)
xi = (xi * _x) / FIXED_1; res += xi * 0x00000b550d19b129d270c44f6f55f027723cbb0000; // add x^(19-1) * (34! * 19^(19-1) / 19!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x00001c877dadc761dc272deb65d4b0000000000000; // sub x^(20-1) * (34! * 20^(20-1) / 20!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000048178ece97479f33a77f2ad22a81b64406c000; // add x^(21-1) * (34! * 21^(21-1) / 21!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x0000b6ca8268b9d810fedf6695ef2f8a6c00000000; // sub x^(22-1) * (34! * 22^(22-1) / 22!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0001d0e76631a5b05d007b8cb72a7c7f11ec36e000; // add x^(23-1) * (34! * 23^(23-1) / 23!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x0004a1c37bd9f85fd9c6c780000000000000000000; // sub x^(24-1) * (34! * 24^(24-1) / 24!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000bd8369f1b702bf491e2ebfcee08250313b65400; // add x^(25-1) * (34! * 25^(25-1) / 25!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x001e5c7c32a9f6c70ab2cb59d9225764d400000000; // sub x^(26-1) * (34! * 26^(26-1) / 26!)
xi = (xi * _x) / FIXED_1; res += xi * 0x004dff5820e165e910f95120a708e742496221e600; // add x^(27-1) * (34! * 27^(27-1) / 27!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x00c8c8f66db1fced378ee50e536000000000000000; // sub x^(28-1) * (34! * 28^(28-1) / 28!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0205db8dffff45bfa2938f128f599dbf16eb11d880; // add x^(29-1) * (34! * 29^(29-1) / 29!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x053a044ebd984351493e1786af38d39a0800000000; // sub x^(30-1) * (34! * 30^(30-1) / 30!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0d86dae2a4cc0f47633a544479735869b487b59c40; // add x^(31-1) * (34! * 31^(31-1) / 31!)
xi = (xi * _x) / FIXED_1; res -= xi * 0x231000000000000000000000000000000000000000; // sub x^(32-1) * (34! * 32^(32-1) / 32!)
xi = (xi * _x) / FIXED_1; res += xi * 0x5b0485a76f6646c2039db1507cdd51b08649680822; // add x^(33-1) * (34! * 33^(33-1) / 33!)
xi = (xi * _x) / FIXED_1; res -= xi * 0xec983c46c49545bc17efa6b5b0055e242200000000; // sub x^(34-1) * (34! * 34^(34-1) / 34!)
return res / 0xde1bc4d19efcac82445da75b00000000; // divide by 34!
}
/**
* @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
* input range: LAMBERT_CONV_RADIUS + 1 <= x <= LAMBERT_POS2_MAXVAL
*/
function lambertPos2(uint256 _x) internal view returns (uint256) {
uint256 x = _x - LAMBERT_CONV_RADIUS - 1;
uint256 i = x / LAMBERT_POS2_SAMPLE;
uint256 a = LAMBERT_POS2_SAMPLE * i;
uint256 b = LAMBERT_POS2_SAMPLE * (i + 1);
uint256 c = lambertArray[i];
uint256 d = lambertArray[i + 1];
return (c * (b - x) + d * (x - a)) / LAMBERT_POS2_SAMPLE;
}
/**
* @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
* input range: LAMBERT_POS2_MAXVAL + 1 <= x <= LAMBERT_POS3_MAXVAL
*/
function lambertPos3(uint256 _x) internal pure returns (uint256) {
uint256 l1 = _x < OPT_LOG_MAX_VAL ? optimalLog(_x) : generalLog(_x);
uint256 l2 = l1 < OPT_LOG_MAX_VAL ? optimalLog(l1) : generalLog(l1);
return (l1 - l2 + l2 * FIXED_1 / l1) * FIXED_1 / _x;
}
/**
* @dev computes W(-x / FIXED_1) / (-x / FIXED_1) * FIXED_1
* input range: 1 <= x <= 1 / e * FIXED_1
* auto-generated via 'PrintFunctionLambertNeg1.py'
*/
function lambertNeg1(uint256 _x) internal pure returns (uint256) {
uint256 xi = _x;
uint256 res = 0;
xi = (xi * _x) / FIXED_1; res += xi * 0x00000000014d29a73a6e7b02c3668c7b0880000000; // add x^(03-1) * (34! * 03^(03-1) / 03!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000000002504a0cd9a7f7215b60f9be4800000000; // add x^(04-1) * (34! * 04^(04-1) / 04!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000000000484d0a1191c0ead267967c7a4a0000000; // add x^(05-1) * (34! * 05^(05-1) / 05!)
xi = (xi * _x) / FIXED_1; res += xi * 0x00000000095ec580d7e8427a4baf26a90a00000000; // add x^(06-1) * (34! * 06^(06-1) / 06!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000000001440b0be1615a47dba6e5b3b1f10000000; // add x^(07-1) * (34! * 07^(07-1) / 07!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000000002d207601f46a99b4112418400000000000; // add x^(08-1) * (34! * 08^(08-1) / 08!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000000066ebaac4c37c622dd8288a7eb1b2000000; // add x^(09-1) * (34! * 09^(09-1) / 09!)
xi = (xi * _x) / FIXED_1; res += xi * 0x00000000ef17240135f7dbd43a1ba10cf200000000; // add x^(10-1) * (34! * 10^(10-1) / 10!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000000233c33c676a5eb2416094a87b3657000000; // add x^(11-1) * (34! * 11^(11-1) / 11!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000000541cde48bc0254bed49a9f8700000000000; // add x^(12-1) * (34! * 12^(12-1) / 12!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000000cae1fad2cdd4d4cb8d73abca0d19a400000; // add x^(13-1) * (34! * 13^(13-1) / 13!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000001edb2aa2f760d15c41ceedba956400000000; // add x^(14-1) * (34! * 14^(14-1) / 14!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000004ba8d20d2dabd386c9529659841a2e200000; // add x^(15-1) * (34! * 15^(15-1) / 15!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000000bac08546b867cdaa20000000000000000000; // add x^(16-1) * (34! * 16^(16-1) / 16!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000001cfa8e70c03625b9db76c8ebf5bbf24820000; // add x^(17-1) * (34! * 17^(17-1) / 17!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000004851d99f82060df265f3309b26f8200000000; // add x^(18-1) * (34! * 18^(18-1) / 18!)
xi = (xi * _x) / FIXED_1; res += xi * 0x00000b550d19b129d270c44f6f55f027723cbb0000; // add x^(19-1) * (34! * 19^(19-1) / 19!)
xi = (xi * _x) / FIXED_1; res += xi * 0x00001c877dadc761dc272deb65d4b0000000000000; // add x^(20-1) * (34! * 20^(20-1) / 20!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000048178ece97479f33a77f2ad22a81b64406c000; // add x^(21-1) * (34! * 21^(21-1) / 21!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0000b6ca8268b9d810fedf6695ef2f8a6c00000000; // add x^(22-1) * (34! * 22^(22-1) / 22!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0001d0e76631a5b05d007b8cb72a7c7f11ec36e000; // add x^(23-1) * (34! * 23^(23-1) / 23!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0004a1c37bd9f85fd9c6c780000000000000000000; // add x^(24-1) * (34! * 24^(24-1) / 24!)
xi = (xi * _x) / FIXED_1; res += xi * 0x000bd8369f1b702bf491e2ebfcee08250313b65400; // add x^(25-1) * (34! * 25^(25-1) / 25!)
xi = (xi * _x) / FIXED_1; res += xi * 0x001e5c7c32a9f6c70ab2cb59d9225764d400000000; // add x^(26-1) * (34! * 26^(26-1) / 26!)
xi = (xi * _x) / FIXED_1; res += xi * 0x004dff5820e165e910f95120a708e742496221e600; // add x^(27-1) * (34! * 27^(27-1) / 27!)
xi = (xi * _x) / FIXED_1; res += xi * 0x00c8c8f66db1fced378ee50e536000000000000000; // add x^(28-1) * (34! * 28^(28-1) / 28!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0205db8dffff45bfa2938f128f599dbf16eb11d880; // add x^(29-1) * (34! * 29^(29-1) / 29!)
xi = (xi * _x) / FIXED_1; res += xi * 0x053a044ebd984351493e1786af38d39a0800000000; // add x^(30-1) * (34! * 30^(30-1) / 30!)
xi = (xi * _x) / FIXED_1; res += xi * 0x0d86dae2a4cc0f47633a544479735869b487b59c40; // add x^(31-1) * (34! * 31^(31-1) / 31!)
xi = (xi * _x) / FIXED_1; res += xi * 0x231000000000000000000000000000000000000000; // add x^(32-1) * (34! * 32^(32-1) / 32!)
xi = (xi * _x) / FIXED_1; res += xi * 0x5b0485a76f6646c2039db1507cdd51b08649680822; // add x^(33-1) * (34! * 33^(33-1) / 33!)
xi = (xi * _x) / FIXED_1; res += xi * 0xec983c46c49545bc17efa6b5b0055e242200000000; // add x^(34-1) * (34! * 34^(34-1) / 34!)
return res / 0xde1bc4d19efcac82445da75b00000000 + _x + FIXED_1; // divide by 34! and then add x^(2-1) * (34! * 2^(2-1) / 2!) + x^(1-1) * (34! * 1^(1-1) / 1!)
}
/**
* @dev computes the weights based on "W(log(hi / lo) * tq / rp) * tq / rp", where "W" is a variation of the Lambert W function.
*/
function balancedWeightsByStake(uint256 _hi, uint256 _lo, uint256 _tq, uint256 _rp, bool _lowerStake) internal view returns (uint32, uint32) {
(_tq, _rp) = safeFactors(_tq, _rp);
uint256 f = _hi.mul(FIXED_1) / _lo;
uint256 g = f < OPT_LOG_MAX_VAL ? optimalLog(f) : generalLog(f);
uint256 x = g.mul(_tq) / _rp;
uint256 y = _lowerStake ? lowerStake(x) : higherStake(x);
return normalizedWeights(y.mul(_tq), _rp.mul(FIXED_1));
}
/**
* @dev reduces "a" and "b" while maintaining their ratio.
*/
function safeFactors(uint256 _a, uint256 _b) internal pure returns (uint256, uint256) {
if (_a <= FIXED_2 && _b <= FIXED_2)
return (_a, _b);
if (_a < FIXED_2)
return (_a * FIXED_2 / _b, FIXED_2);
if (_b < FIXED_2)
return (FIXED_2, _b * FIXED_2 / _a);
uint256 c = _a > _b ? _a : _b;
uint256 n = floorLog2(c / FIXED_1);
return (_a >> n, _b >> n);
}
/**
* @dev computes "MAX_WEIGHT * a / (a + b)" and "MAX_WEIGHT * b / (a + b)".
*/
function normalizedWeights(uint256 _a, uint256 _b) internal pure returns (uint32, uint32) {
if (_a <= _b)
return accurateWeights(_a, _b);
(uint32 y, uint32 x) = accurateWeights(_b, _a);
return (x, y);
}
/**
* @dev computes "MAX_WEIGHT * a / (a + b)" and "MAX_WEIGHT * b / (a + b)", assuming that "a <= b".
*/
function accurateWeights(uint256 _a, uint256 _b) internal pure returns (uint32, uint32) {
if (_a > MAX_UNF_WEIGHT) {
uint256 c = _a / (MAX_UNF_WEIGHT + 1) + 1;
_a /= c;
_b /= c;
}
uint256 x = roundDiv(_a * MAX_WEIGHT, _a.add(_b));
uint256 y = MAX_WEIGHT - x;
return (uint32(x), uint32(y));
}
/**
* @dev computes the nearest integer to a given quotient without overflowing or underflowing.
*/
function roundDiv(uint256 _n, uint256 _d) internal pure returns (uint256) {
return _n / _d + _n % _d / (_d - _d / 2);
}
/**
* @dev deprecated, backward compatibility
*/
function calculatePurchaseReturn(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256)
{
return purchaseTargetAmount(_supply, _reserveBalance, _reserveWeight, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function calculateSaleReturn(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256)
{
return saleTargetAmount(_supply, _reserveBalance, _reserveWeight, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function calculateCrossReserveReturn(uint256 _sourceReserveBalance,
uint32 _sourceReserveWeight,
uint256 _targetReserveBalance,
uint32 _targetReserveWeight,
uint256 _amount)
public view returns (uint256)
{
return crossReserveTargetAmount(_sourceReserveBalance, _sourceReserveWeight, _targetReserveBalance, _targetReserveWeight, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function calculateCrossConnectorReturn(uint256 _sourceReserveBalance,
uint32 _sourceReserveWeight,
uint256 _targetReserveBalance,
uint32 _targetReserveWeight,
uint256 _amount)
public view returns (uint256)
{
return crossReserveTargetAmount(_sourceReserveBalance, _sourceReserveWeight, _targetReserveBalance, _targetReserveWeight, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function calculateFundCost(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256)
{
return fundCost(_supply, _reserveBalance, _reserveRatio, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function calculateLiquidateReturn(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256)
{
return liquidateReserveAmount(_supply, _reserveBalance, _reserveRatio, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function purchaseRate(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256)
{
return purchaseTargetAmount(_supply, _reserveBalance, _reserveWeight, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function saleRate(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveWeight,
uint256 _amount)
public view returns (uint256)
{
return saleTargetAmount(_supply, _reserveBalance, _reserveWeight, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function crossReserveRate(uint256 _sourceReserveBalance,
uint32 _sourceReserveWeight,
uint256 _targetReserveBalance,
uint32 _targetReserveWeight,
uint256 _amount)
public view returns (uint256)
{
return crossReserveTargetAmount(_sourceReserveBalance, _sourceReserveWeight, _targetReserveBalance, _targetReserveWeight, _amount);
}
/**
* @dev deprecated, backward compatibility
*/
function liquidateRate(uint256 _supply,
uint256 _reserveBalance,
uint32 _reserveRatio,
uint256 _amount)
public view returns (uint256)
{
return liquidateReserveAmount(_supply, _reserveBalance, _reserveRatio, _amount);
}
}