Transaction Hash:
Block:
11985571 at Mar-06-2021 02:58:36 PM +UTC
Transaction Fee:
0.00874209 ETH
$13.91
Gas Used:
124,887 Gas / 70 Gwei
Emitted Events:
79 |
DSToken.Transfer( _from=[Sender] 0x4a7407c7db5f0d6795c53c81961ee42f65088399, _to=0x0000000000000000000000000000000000000000, _value=20554121700941215649 )
|
80 |
DSToken.Destruction( _amount=20554121700941215649 )
|
81 |
Uni.Transfer( from=[Receiver] StandardPoolConverter, to=[Sender] 0x4a7407c7db5f0d6795c53c81961ee42f65088399, amount=23093254977940576814 )
|
82 |
StandardPoolConverter.LiquidityRemoved( _provider=[Sender] 0x4a7407c7db5f0d6795c53c81961ee42f65088399, _reserveToken=Uni, _amount=23093254977940576814, _newBalance=255139291539885984596951, _newSupply=227086396179842682999172 )
|
83 |
StandardPoolConverter.TokenRateUpdate( _token1=DSToken, _token2=Uni, _rateN=255139291539885984596951, _rateD=227086396179842682999172 )
|
84 |
SmartToken.Transfer( _from=[Receiver] StandardPoolConverter, _to=[Sender] 0x4a7407c7db5f0d6795c53c81961ee42f65088399, _value=84477266723394664503 )
|
85 |
StandardPoolConverter.LiquidityRemoved( _provider=[Sender] 0x4a7407c7db5f0d6795c53c81961ee42f65088399, _reserveToken=SmartToken, _amount=84477266723394664503, _newBalance=933323171792866399773563, _newSupply=227086396179842682999172 )
|
86 |
StandardPoolConverter.TokenRateUpdate( _token1=DSToken, _token2=SmartToken, _rateN=933323171792866399773563, _rateD=227086396179842682999172 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x1F573D6F...d79a7FF1C | |||||
0x1f9840a8...C4201F984 | |||||
0x4A7407c7...f65088399 |
8.064474723 Eth
Nonce: 10
|
8.055732633 Eth
Nonce: 11
| 0.00874209 | ||
0x52bc44d5...b7d7bE3b5
Miner
| (Nanopool) | 3,267.699446313535542994 Eth | 3,267.708188403535542994 Eth | 0.00874209 | |
0x8b3082e2...D963cA307 | |||||
0xc1956f06...203645Ba4 | (Bancor: Converter 501) |
Execution Trace
StandardPoolConverter.removeLiquidity( _amount=20554121700941215649, _reserveTokens=[0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984, 0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C], _reserveMinReturnAmounts=[22631389878381764000, 82787721388926770000] ) => ( [23093254977940576814, 84477266723394664503] )
-
DSToken.STATICCALL( )
-
DSToken.STATICCALL( )
-
DSToken.destroy( _from=0x4A7407c7Db5F0D6795C53c81961Ee42f65088399, _amount=20554121700941215649 )
-
Uni.balanceOf( account=0xc1956f06ffA45A47423aCF4A3422Dc2203645Ba4 ) => ( 255162384794863925173765 )
-
SmartToken.balanceOf( 0xc1956f06ffA45A47423aCF4A3422Dc2203645Ba4 ) => ( 933407649059589794438066 )
-
Uni.transfer( dst=0x4A7407c7Db5F0D6795C53c81961Ee42f65088399, rawAmount=23093254977940576814 ) => ( True )
-
SmartToken.transfer( _to=0x4A7407c7Db5F0D6795C53c81961Ee42f65088399, _value=84477266723394664503 ) => ( success=True )
removeLiquidity[StandardPoolConverter (ln:1695)]
removeLiquidity[StandardPoolConverter (ln:1699)]
removeLiquidity[StandardPoolConverter (ln:1699)]
File 1 of 4: StandardPoolConverter
File 2 of 4: DSToken
File 3 of 4: Uni
File 4 of 4: SmartToken
// File: solidity/contracts/converter/ConverterVersion.sol // SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity 0.6.12; contract ConverterVersion { uint16 public constant version = 44; } // File: solidity/contracts/utility/interfaces/IOwned.sol pragma solidity 0.6.12; /* Owned contract interface */ interface IOwned { // this function isn't since the compiler emits automatically generated getter functions as external function owner() external view returns (address); function transferOwnership(address _newOwner) external; function acceptOwnership() external; } // File: solidity/contracts/converter/interfaces/IConverterAnchor.sol pragma solidity 0.6.12; /* Converter Anchor interface */ interface IConverterAnchor is IOwned { } // File: solidity/contracts/token/interfaces/IERC20Token.sol pragma solidity 0.6.12; /* ERC20 Standard Token interface */ interface IERC20Token { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address _owner) external view returns (uint256); function allowance(address _owner, address _spender) external view returns (uint256); function transfer(address _to, uint256 _value) external returns (bool); function transferFrom( address _from, address _to, uint256 _value ) external returns (bool); function approve(address _spender, uint256 _value) external returns (bool); } // File: solidity/contracts/converter/interfaces/IConverter.sol pragma solidity 0.6.12; /* Converter interface */ interface IConverter is IOwned { function converterType() external pure returns (uint16); function anchor() external view returns (IConverterAnchor); function isActive() external view returns (bool); function targetAmountAndFee( IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount ) external view returns (uint256, uint256); function convert( IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address payable _beneficiary ) external payable returns (uint256); function conversionFee() external view returns (uint32); function maxConversionFee() external view returns (uint32); function reserveBalance(IERC20Token _reserveToken) external view returns (uint256); receive() external payable; function transferAnchorOwnership(address _newOwner) external; function acceptAnchorOwnership() external; function setConversionFee(uint32 _conversionFee) external; function withdrawTokens( IERC20Token _token, address _to, uint256 _amount ) external; function withdrawETH(address payable _to) external; function addReserve(IERC20Token _token, uint32 _ratio) external; // deprecated, backward compatibility function token() external view returns (IConverterAnchor); function transferTokenOwnership(address _newOwner) external; function acceptTokenOwnership() external; function connectors(IERC20Token _address) external view returns ( uint256, uint32, bool, bool, bool ); function getConnectorBalance(IERC20Token _connectorToken) external view returns (uint256); function connectorTokens(uint256 _index) external view returns (IERC20Token); function connectorTokenCount() external view returns (uint16); /** * @dev triggered when the converter is activated * * @param _type converter type * @param _anchor converter anchor * @param _activated true if the converter was activated, false if it was deactivated */ event Activation(uint16 indexed _type, IConverterAnchor indexed _anchor, bool indexed _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 input amount in units of the source token * @param _return output amount minus conversion fee in units of the target token * @param _conversionFee conversion fee in units of the target token */ event Conversion( IERC20Token indexed _fromToken, IERC20Token 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 * * @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(IERC20Token indexed _token1, IERC20Token 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); } // File: solidity/contracts/converter/interfaces/IConverterUpgrader.sol pragma solidity 0.6.12; /* Converter Upgrader interface */ interface IConverterUpgrader { function upgrade(bytes32 _version) external; function upgrade(uint16 _version) external; } // File: solidity/contracts/token/interfaces/IDSToken.sol pragma solidity 0.6.12; /* DSToken interface */ interface IDSToken is IConverterAnchor, IERC20Token { function issue(address _to, uint256 _amount) external; function destroy(address _from, uint256 _amount) external; } // File: solidity/contracts/utility/Owned.sol pragma solidity 0.6.12; /** * @dev This contract provides support and utilities for contract ownership. */ contract Owned is IOwned { address public override 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 override ownerOnly { require(_newOwner != owner, "ERR_SAME_OWNER"); newOwner = _newOwner; } /** * @dev used by a new owner to accept an ownership transfer */ function acceptOwnership() public override { require(msg.sender == newOwner, "ERR_ACCESS_DENIED"); emit OwnerUpdate(owner, newOwner); owner = newOwner; newOwner = address(0); } } // File: solidity/contracts/utility/Utils.sol pragma solidity 0.6.12; /** * @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: solidity/contracts/utility/interfaces/IContractRegistry.sol pragma solidity 0.6.12; /* Contract Registry interface */ interface IContractRegistry { function addressOf(bytes32 _contractName) external view returns (address); } // File: solidity/contracts/utility/ContractRegistryClient.sol pragma solidity 0.6.12; /** * @dev This is the 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(address(_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 != registry && address(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: solidity/contracts/utility/ReentrancyGuard.sol pragma solidity 0.6.12; /** * @dev This contract provides protection against calling a function * (directly or indirectly) from within itself. */ contract ReentrancyGuard { uint256 private constant UNLOCKED = 1; uint256 private constant LOCKED = 2; // LOCKED while protected code is being executed, UNLOCKED otherwise uint256 private state = UNLOCKED; /** * @dev ensures instantiation only by sub-contracts */ constructor() internal {} // protects a function against reentrancy attacks modifier protected() { _protected(); state = LOCKED; _; state = UNLOCKED; } // error message binary size optimization function _protected() internal view { require(state == UNLOCKED, "ERR_REENTRANCY"); } } // File: solidity/contracts/utility/SafeMath.sol pragma solidity 0.6.12; /** * @dev This library supports 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/utility/TokenHandler.sol pragma solidity 0.6.12; 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 { (bool success, bytes memory data) = address(_token).call( abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "ERR_APPROVE_FAILED"); } /** * @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 { (bool success, bytes memory data) = address(_token).call( abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "ERR_TRANSFER_FAILED"); } /** * @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 { (bool success, bytes memory data) = address(_token).call( abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "ERR_TRANSFER_FROM_FAILED"); } } // File: solidity/contracts/utility/interfaces/ITokenHolder.sol pragma solidity 0.6.12; /* Token Holder interface */ interface ITokenHolder is IOwned { function withdrawTokens( IERC20Token _token, address _to, uint256 _amount ) external; } // File: solidity/contracts/utility/TokenHolder.sol pragma solidity 0.6.12; /** * @dev This contract provides a safety mechanism for allowing the owner to * send tokens that were sent to the contract by mistake back to the sender. * * We consider every contract to be a 'token holder' since it's currently not possible * for a contract to deny receiving tokens. * * 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 virtual override ownerOnly validAddress(address(_token)) validAddress(_to) notThis(_to) { safeTransfer(_token, _to, _amount); } } // File: solidity/contracts/utility/Math.sol pragma solidity 0.6.12; /** * @dev This library provides a set of complex math operations. */ library Math { /** * @dev returns the largest integer smaller than or equal to the square root of a positive integer * * @param _num a positive integer * * @return the largest integer smaller than or equal to the square root of the positive integer */ function floorSqrt(uint256 _num) internal pure returns (uint256) { uint256 x = _num / 2 + 1; uint256 y = (x + _num / x) / 2; while (x > y) { x = y; y = (x + _num / x) / 2; } return x; } /** * @dev returns the smallest integer larger than or equal to the square root of a positive integer * * @param _num a positive integer * * @return the smallest integer larger than or equal to the square root of the positive integer */ function ceilSqrt(uint256 _num) internal pure returns (uint256) { uint256 x = floorSqrt(_num); return x * x == _num ? x : x + 1; } /** * @dev computes a reduced-scalar ratio * * @param _n ratio numerator * @param _d ratio denominator * @param _max maximum desired scalar * * @return ratio's numerator and denominator */ function reducedRatio( uint256 _n, uint256 _d, uint256 _max ) internal pure returns (uint256, uint256) { (uint256 n, uint256 d) = (_n, _d); if (n > _max || d > _max) { (n, d) = normalizedRatio(n, d, _max); } if (n != d) { return (n, d); } return (1, 1); } /** * @dev computes "scale * a / (a + b)" and "scale * b / (a + b)". */ function normalizedRatio( uint256 _a, uint256 _b, uint256 _scale ) internal pure returns (uint256, uint256) { if (_a <= _b) { return accurateRatio(_a, _b, _scale); } (uint256 y, uint256 x) = accurateRatio(_b, _a, _scale); return (x, y); } /** * @dev computes "scale * a / (a + b)" and "scale * b / (a + b)", assuming that "a <= b". */ function accurateRatio( uint256 _a, uint256 _b, uint256 _scale ) internal pure returns (uint256, uint256) { uint256 maxVal = uint256(-1) / _scale; if (_a > maxVal) { uint256 c = _a / (maxVal + 1) + 1; _a /= c; // we can now safely compute `_a * _scale` _b /= c; } if (_a != _b) { uint256 n = _a * _scale; uint256 d = _a + _b; // can overflow if (d >= _a) { // no overflow in `_a + _b` uint256 x = roundDiv(n, d); // we can now safely compute `_scale - x` uint256 y = _scale - x; return (x, y); } if (n < _b - (_b - _a) / 2) { return (0, _scale); // `_a * _scale < (_a + _b) / 2 < MAX_UINT256 < _a + _b` } return (1, _scale - 1); // `(_a + _b) / 2 < _a * _scale < MAX_UINT256 < _a + _b` } return (_scale / 2, _scale / 2); // allow reduction to `(1, 1)` in the calling function } /** * @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 returns the average number of decimal digits in a given list of positive integers * * @param _values list of positive integers * * @return the average number of decimal digits in the given list of positive integers */ function geometricMean(uint256[] memory _values) internal pure returns (uint256) { uint256 numOfDigits = 0; uint256 length = _values.length; for (uint256 i = 0; i < length; i++) { numOfDigits += decimalLength(_values[i]); } return uint256(10)**(roundDivUnsafe(numOfDigits, length) - 1); } /** * @dev returns the number of decimal digits in a given positive integer * * @param _x positive integer * * @return the number of decimal digits in the given positive integer */ function decimalLength(uint256 _x) internal pure returns (uint256) { uint256 y = 0; for (uint256 x = _x; x > 0; x /= 10) { y++; } return y; } /** * @dev returns the nearest integer to a given quotient * the computation is overflow-safe assuming that the input is sufficiently small * * @param _n quotient numerator * @param _d quotient denominator * * @return the nearest integer to the given quotient */ function roundDivUnsafe(uint256 _n, uint256 _d) internal pure returns (uint256) { return (_n + _d / 2) / _d; } /** * @dev returns the larger of two values * * @param _val1 the first value * @param _val2 the second value */ function max(uint256 _val1, uint256 _val2) internal pure returns (uint256) { return _val1 > _val2 ? _val1 : _val2; } } // File: solidity/contracts/utility/Time.sol pragma solidity 0.6.12; /* Time implementing contract */ contract Time { /** * @dev returns the current time */ function time() internal view virtual returns (uint256) { return block.timestamp; } } // File: solidity/contracts/converter/types/standard-pool/StandardPoolConverter.sol pragma solidity 0.6.12; /** * @dev This contract is a specialized version of the converter, which is * optimized for a liquidity pool that has 2 reserves with 50%/50% weights. */ contract StandardPoolConverter is ConverterVersion, IConverter, TokenHandler, TokenHolder, ContractRegistryClient, ReentrancyGuard, Time { using SafeMath for uint256; using Math for *; IERC20Token private constant ETH_RESERVE_ADDRESS = IERC20Token(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); uint256 private constant MAX_UINT128 = 2**128 - 1; uint256 private constant MAX_UINT112 = 2**112 - 1; uint256 private constant MAX_UINT32 = 2**32 - 1; uint256 private constant AVERAGE_RATE_PERIOD = 10 minutes; uint32 private constant PPM_RESOLUTION = 1000000; uint256 private __reserveBalances; IERC20Token[] private __reserveTokens; mapping(IERC20Token => uint256) private __reserveIds; IConverterAnchor public override anchor; // converter anchor contract uint32 public override maxConversionFee; // maximum conversion fee, represented in ppm, 0...1000000 uint32 public override conversionFee; // current conversion fee, represented in ppm, 0...maxConversionFee // average rate details: // bits 0...111 represent the numerator of the rate between reserve token 0 and reserve token 1 // bits 111...223 represent the denominator of the rate between reserve token 0 and reserve token 1 // bits 224...255 represent the update-time of the rate between reserve token 0 and reserve token 1 // where `numerator / denominator` gives the worth of one reserve token 0 in units of reserve token 1 uint256 public averageRateInfo; /** * @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, IERC20Token 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, IERC20Token indexed _reserveToken, uint256 _amount, uint256 _newBalance, uint256 _newSupply ); /** * @dev initializes a new StandardPoolConverter 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 ) public ContractRegistryClient(_registry) validAddress(address(_anchor)) 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(__reserveIds[_address] != 0, "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 <= PPM_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 == PPM_RESOLUTION / 2, "ERR_INVALID_RESERVE_WEIGHT"); } /** * @dev returns the converter type * * @return see the converter types in the the main contract doc */ function converterType() public pure override returns (uint16) { return 3; } /** * @dev deposits ether * can only be called if the converter has an ETH reserve */ receive() external payable override validReserve(ETH_RESERVE_ADDRESS) {} /** * @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 payable _to) public override protected ownerOnly validReserve(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(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 returns true if the converter is active, false otherwise * * @return true if the converter is active, false otherwise */ function isActive() public view virtual override 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 override 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 virtual override ownerOnly { // verify the the converter has exactly two reserves require(reserveTokenCount() == 2, "ERR_INVALID_RESERVE_COUNT"); anchor.acceptOwnership(); syncReserveBalances(); emit Activation(converterType(), anchor, true); } /** * @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 override 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 override(IConverter, TokenHolder) protected ownerOnly { address converterUpgrader = addressOf(CONVERTER_UPGRADER); uint256 reserveId = __reserveIds[_token]; // 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(reserveId == 0 || !isActive() || owner == converterUpgrader, "ERR_ACCESS_DENIED"); super.withdrawTokens(_token, _to, _amount); // if the token is a reserve token, sync the reserve balance if (reserveId != 0) { 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)); // trigger de-activation event emit Activation(converterType(), anchor, false); transferOwnership(address(converterUpgrader)); converterUpgrader.upgrade(version); acceptOwnership(); } /** * @dev returns the number of reserve tokens * 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 returns the array of reserve tokens * * @return array of reserve tokens */ function reserveTokens() public view returns (IERC20Token[] memory) { return __reserveTokens; } /** * @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 virtual override ownerOnly inactive validAddress(address(_token)) notThis(address(_token)) validReserveWeight(_weight) { // validate input require(address(_token) != address(anchor) && __reserveIds[_token] == 0, "ERR_INVALID_RESERVE"); require(reserveTokenCount() < 2, "ERR_INVALID_RESERVE_COUNT"); __reserveTokens.push(_token); __reserveIds[_token] = __reserveTokens.length; } /** * @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 PPM_RESOLUTION / 2; } /** * @dev returns the balance of a given reserve token * * @param _reserveToken reserve token contract address * * @return the balance of the given reserve token */ function reserveBalance(IERC20Token _reserveToken) public view override returns (uint256) { uint256 reserveId = __reserveIds[_reserveToken]; require(reserveId != 0, "ERR_INVALID_RESERVE"); return reserveBalance(reserveId); } /** * @dev returns the balances of both reserve tokens * * @return the balances of both reserve tokens */ function reserveBalances() public view returns (uint256, uint256) { return reserveBalances(1, 2); } /** * @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 payable _beneficiary ) public payable override protected only(BANCOR_NETWORK) returns (uint256) { // validate input require(_sourceToken != _targetToken, "ERR_SAME_SOURCE_TARGET"); return doConvert(_sourceToken, _targetToken, _amount, _trader, _beneficiary); } /** * @dev returns the conversion fee for a given target amount * * @param _targetAmount target amount * * @return conversion fee */ function calculateFee(uint256 _targetAmount) internal view returns (uint256) { return _targetAmount.mul(conversionFee) / PPM_RESOLUTION; } /** * @dev loads the stored reserve balance for a given reserve id * * @param _reserveId reserve id */ function reserveBalance(uint256 _reserveId) internal view returns (uint256) { return decodeReserveBalance(__reserveBalances, _reserveId); } /** * @dev loads the stored reserve balances * * @param _sourceId source reserve id * @param _targetId target reserve id */ function reserveBalances(uint256 _sourceId, uint256 _targetId) internal view returns (uint256, uint256) { require((_sourceId == 1 && _targetId == 2) || (_sourceId == 2 && _targetId == 1), "ERR_INVALID_RESERVES"); return decodeReserveBalances(__reserveBalances, _sourceId, _targetId); } /** * @dev stores the stored reserve balance for a given reserve id * * @param _reserveId reserve id * @param _reserveBalance reserve balance */ function setReserveBalance(uint256 _reserveId, uint256 _reserveBalance) internal { require(_reserveBalance <= MAX_UINT128, "ERR_RESERVE_BALANCE_OVERFLOW"); uint256 otherBalance = decodeReserveBalance(__reserveBalances, 3 - _reserveId); __reserveBalances = encodeReserveBalances(_reserveBalance, _reserveId, otherBalance, 3 - _reserveId); } /** * @dev stores the stored reserve balances * * @param _sourceId source reserve id * @param _targetId target reserve id * @param _sourceBalance source reserve balance * @param _targetBalance target reserve balance */ function setReserveBalances( uint256 _sourceId, uint256 _targetId, uint256 _sourceBalance, uint256 _targetBalance ) internal { require(_sourceBalance <= MAX_UINT128 && _targetBalance <= MAX_UINT128, "ERR_RESERVE_BALANCE_OVERFLOW"); __reserveBalances = encodeReserveBalances(_sourceBalance, _sourceId, _targetBalance, _targetId); } /** * @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 { uint256 reserveId = __reserveIds[_reserveToken]; uint256 balance = _reserveToken == ETH_RESERVE_ADDRESS ? address(this).balance : _reserveToken.balanceOf(address(this)); setReserveBalance(reserveId, balance); } /** * @dev syncs all stored reserve balances */ function syncReserveBalances() internal { IERC20Token _reserveToken0 = __reserveTokens[0]; IERC20Token _reserveToken1 = __reserveTokens[1]; uint256 balance0 = _reserveToken0 == ETH_RESERVE_ADDRESS ? address(this).balance : _reserveToken0.balanceOf(address(this)); uint256 balance1 = _reserveToken1 == ETH_RESERVE_ADDRESS ? address(this).balance : _reserveToken1.balanceOf(address(this)); setReserveBalances(1, 2, balance0, balance1); } /** * @dev syncs all stored reserve balances, excluding a given amount of ether from the ether reserve balance (if relevant) * * @param _value amount of ether to exclude from the ether reserve balance (if relevant) */ function syncReserveBalances(uint256 _value) internal { IERC20Token _reserveToken0 = __reserveTokens[0]; IERC20Token _reserveToken1 = __reserveTokens[1]; uint256 balance0 = _reserveToken0 == ETH_RESERVE_ADDRESS ? address(this).balance - _value : _reserveToken0.balanceOf(address(this)); uint256 balance1 = _reserveToken1 == ETH_RESERVE_ADDRESS ? address(this).balance - _value : _reserveToken1.balanceOf(address(this)); setReserveBalances(1, 2, balance0, balance1); } /** * @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 { emit Conversion(_sourceToken, _targetToken, _trader, _amount, _returnAmount, int256(_feeAmount)); } /** * @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 override active returns (uint256, uint256) { uint256 sourceId = __reserveIds[_sourceToken]; uint256 targetId = __reserveIds[_targetToken]; (uint256 sourceBalance, uint256 targetBalance) = reserveBalances(sourceId, targetId); uint256 amount = crossReserveTargetAmount(sourceBalance, targetBalance, _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 * * @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 payable _beneficiary ) internal returns (uint256) { // update the recent average rate updateRecentAverageRate(); uint256 sourceId = __reserveIds[_sourceToken]; uint256 targetId = __reserveIds[_targetToken]; (uint256 sourceBalance, uint256 targetBalance) = reserveBalances(sourceId, targetId); uint256 targetAmount = crossReserveTargetAmount(sourceBalance, targetBalance, _amount); // get the target amount minus the conversion fee and the conversion fee uint256 fee = calculateFee(targetAmount); uint256 amount = targetAmount - fee; // 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 assert(amount < targetBalance); // ensure that the input amount was already deposited uint256 actualSourceBalance; if (_sourceToken == ETH_RESERVE_ADDRESS) { actualSourceBalance = address(this).balance; require(msg.value == _amount, "ERR_ETH_AMOUNT_MISMATCH"); } else { actualSourceBalance = _sourceToken.balanceOf(address(this)); require(msg.value == 0 && actualSourceBalance.sub(sourceBalance) >= _amount, "ERR_INVALID_AMOUNT"); } // sync the reserve balances setReserveBalances(sourceId, targetId, actualSourceBalance, targetBalance - 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 dispatchTokenRateUpdateEvents(_sourceToken, _targetToken, actualSourceBalance, targetBalance - amount); return amount; } /** * @dev returns the recent average rate of 1 `_token` in the other reserve token units * * @param _token token to get the rate for * @return recent average rate between the reserves (numerator) * @return recent average rate between the reserves (denominator) */ function recentAverageRate(IERC20Token _token) external view validReserve(_token) returns (uint256, uint256) { // get the recent average rate of reserve 0 uint256 rate = calcRecentAverageRate(averageRateInfo); uint256 rateN = decodeAverageRateN(rate); uint256 rateD = decodeAverageRateD(rate); if (_token == __reserveTokens[0]) { return (rateN, rateD); } return (rateD, rateN); } /** * @dev updates the recent average rate if needed */ function updateRecentAverageRate() internal { uint256 averageRateInfo1 = averageRateInfo; uint256 averageRateInfo2 = calcRecentAverageRate(averageRateInfo1); if (averageRateInfo1 != averageRateInfo2) { averageRateInfo = averageRateInfo2; } } /** * @dev returns the recent average rate of 1 reserve token 0 in reserve token 1 units * * @param _averageRateInfo a local copy of the `averageRateInfo` state-variable * @return recent average rate between the reserves */ function calcRecentAverageRate(uint256 _averageRateInfo) internal view returns (uint256) { // get the previous average rate and its update-time uint256 prevAverageRateT = decodeAverageRateT(_averageRateInfo); uint256 prevAverageRateN = decodeAverageRateN(_averageRateInfo); uint256 prevAverageRateD = decodeAverageRateD(_averageRateInfo); // get the elapsed time since the previous average rate was calculated uint256 currentTime = time(); uint256 timeElapsed = currentTime - prevAverageRateT; // if the previous average rate was calculated in the current block, the average rate remains unchanged if (timeElapsed == 0) { return _averageRateInfo; } // get the current rate between the reserves (uint256 currentRateD, uint256 currentRateN) = reserveBalances(); // if the previous average rate was calculated a while ago or never, the average rate is equal to the current rate if (timeElapsed >= AVERAGE_RATE_PERIOD || prevAverageRateT == 0) { (currentRateN, currentRateD) = Math.reducedRatio(currentRateN, currentRateD, MAX_UINT112); return encodeAverageRateInfo(currentTime, currentRateN, currentRateD); } uint256 x = prevAverageRateD.mul(currentRateN); uint256 y = prevAverageRateN.mul(currentRateD); // since we know that timeElapsed < AVERAGE_RATE_PERIOD, we can avoid using SafeMath: uint256 newRateN = y.mul(AVERAGE_RATE_PERIOD - timeElapsed).add(x.mul(timeElapsed)); uint256 newRateD = prevAverageRateD.mul(currentRateD).mul(AVERAGE_RATE_PERIOD); (newRateN, newRateD) = Math.reducedRatio(newRateN, newRateD, MAX_UINT112); return encodeAverageRateInfo(currentTime, newRateN, newRateD); } /** * @dev increases the pool's liquidity and mints new shares in the pool to the caller * this version receives the two reserve amounts as separate args * * @param _reserve1Amount amount of the first reserve token * @param _reserve2Amount amount of the second reserve token * @param _minReturn token minimum return-amount * * @return amount of pool tokens issued */ function addLiquidity(uint256 _reserve1Amount, uint256 _reserve2Amount, uint256 _minReturn) public payable returns (uint256) { uint256[] memory reserveAmounts = new uint256[](2); reserveAmounts[0] = _reserve1Amount; reserveAmounts[1] = _reserve2Amount; return addLiquidity(__reserveTokens, reserveAmounts, _minReturn); } /** * @dev increases the pool's liquidity and mints new shares in the pool to the caller * * @param _reserveTokens address of each reserve token * @param _reserveAmounts amount of each reserve token * @param _minReturn token minimum return-amount * * @return amount of pool tokens issued */ function addLiquidity( IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _minReturn ) public payable protected active returns (uint256) { // 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 < 2; 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(__reserveIds[ETH_RESERVE_ADDRESS] != 0, "ERR_NO_ETH_RESERVE"); } // save a local copy of the pool token IDSToken poolToken = IDSToken(address(anchor)); // get the total supply uint256 totalSupply = poolToken.totalSupply(); // sync the balances to ensure no mismatch syncReserveBalances(msg.value); uint256[2] memory oldReserveBalances; uint256[2] memory newReserveBalances; (oldReserveBalances[0], oldReserveBalances[1]) = reserveBalances(); // calculate the amount of pool tokens to mint uint256 amount; uint256[] memory reserveAmounts = new uint256[](2); if (totalSupply == 0) { for (uint256 i = 0; i < 2; i++) { reserveAmounts[i] = _reserveAmounts[i]; } amount = Math.geometricMean(reserveAmounts); } else { uint256 index = (_reserveAmounts[0].mul(oldReserveBalances[1]) < _reserveAmounts[1].mul(oldReserveBalances[0])) ? 0 : 1; amount = fundSupplyAmount(totalSupply, oldReserveBalances[index], _reserveAmounts[index]); for (uint256 i = 0; i < 2; i++) { reserveAmounts[i] = fundCost(totalSupply, oldReserveBalances[i], amount); } } uint256 newPoolTokenSupply = totalSupply.add(amount); for (uint256 i = 0; i < 2; i++) { IERC20Token reserveToken = _reserveTokens[i]; uint256 reserveAmount = reserveAmounts[i]; 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, address(this), reserveAmount); } else if (_reserveAmounts[i] > reserveAmount) { // transfer the extra amount of ETH back to the user msg.sender.transfer(_reserveAmounts[i] - reserveAmount); } // save the new reserve balance newReserveBalances[i] = oldReserveBalances[i].add(reserveAmount); emit LiquidityAdded(msg.sender, reserveToken, reserveAmount, newReserveBalances[i], newPoolTokenSupply); // dispatch the `TokenRateUpdate` event for the pool token emit TokenRateUpdate(poolToken, reserveToken, newReserveBalances[i], newPoolTokenSupply); } // set the reserve balances setReserveBalances(1, 2, newReserveBalances[0], newReserveBalances[1]); // 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 poolToken.issue(msg.sender, amount); // return the amount of pool tokens issued return amount; } /** * @dev decreases the pool's liquidity and burns the caller's shares in the pool * this version receives the two minimum return amounts as separate args * * @param _amount token amount * @param _reserve1MinReturn minimum return for the first reserve token * @param _reserve2MinReturn minimum return for the second reserve token * * @return the first reserve amount returned * @return the second reserve amount returned */ function removeLiquidity(uint256 _amount, uint256 _reserve1MinReturn, uint256 _reserve2MinReturn) public returns (uint256, uint256) { uint256[] memory minReturnAmounts = new uint256[](2); minReturnAmounts[0] = _reserve1MinReturn; minReturnAmounts[1] = _reserve2MinReturn; uint256[] memory reserveAmounts = removeLiquidity(_amount, __reserveTokens, minReturnAmounts); return (reserveAmounts[0], reserveAmounts[1]); } /** * @dev decreases the pool's liquidity and burns the caller's shares in the pool * * @param _amount token amount * @param _reserveTokens address of each reserve token * @param _reserveMinReturnAmounts minimum return-amount of each reserve token * * @return the amount of each reserve token granted for the given amount of pool tokens */ function removeLiquidity( uint256 _amount, IERC20Token[] memory _reserveTokens, uint256[] memory _reserveMinReturnAmounts ) public protected active returns (uint256[] memory) { // verify the user input bool inputRearranged = verifyLiquidityInput(_reserveTokens, _reserveMinReturnAmounts, _amount); // save a local copy of the pool token IDSToken poolToken = IDSToken(address(anchor)); // get the total supply BEFORE destroying the user tokens uint256 totalSupply = poolToken.totalSupply(); // destroy the user tokens poolToken.destroy(msg.sender, _amount); // sync the balances to ensure no mismatch syncReserveBalances(); uint256 newPoolTokenSupply = totalSupply.sub(_amount); uint256[] memory reserveAmounts = removeLiquidityReserveAmounts(_amount, _reserveTokens, totalSupply); uint256[2] memory oldReserveBalances; uint256[2] memory newReserveBalances; (oldReserveBalances[0], oldReserveBalances[1]) = reserveBalances(); for (uint256 i = 0; i < 2; i++) { IERC20Token reserveToken = _reserveTokens[i]; uint256 reserveAmount = reserveAmounts[i]; require(reserveAmount >= _reserveMinReturnAmounts[i], "ERR_ZERO_TARGET_AMOUNT"); // save the new reserve balance newReserveBalances[i] = oldReserveBalances[i].sub(reserveAmount); // 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, newReserveBalances[i], newPoolTokenSupply); // dispatch the `TokenRateUpdate` event for the pool token emit TokenRateUpdate(poolToken, reserveToken, newReserveBalances[i], newPoolTokenSupply); } // set the reserve balances setReserveBalances(1, 2, newReserveBalances[0], newReserveBalances[1]); if (inputRearranged) { uint256 tempReserveAmount = reserveAmounts[0]; reserveAmounts[0] = reserveAmounts[1]; reserveAmounts[1] = tempReserveAmount; } // return the amount of each reserve token granted for the given amount of pool tokens return reserveAmounts; } /** * @dev given the amount of one of the reserve tokens to add liquidity of, * returns the required amount of each one of the other reserve tokens * since an empty pool can be funded with any list of non-zero input amounts, * this function assumes that the pool is not empty (has already been funded) * * @param _reserveTokens address of each reserve token * @param _reserveTokenIndex index of the relevant reserve token * @param _reserveAmount amount of the relevant reserve token * * @return the required amount of each one of the reserve tokens */ function addLiquidityCost( IERC20Token[] memory _reserveTokens, uint256 _reserveTokenIndex, uint256 _reserveAmount ) public view returns (uint256[] memory) { uint256[] memory _reserveAmounts = new uint256[](2); uint256[] memory _reserveBalances = new uint256[](2); uint256 reserve0Id = __reserveIds[_reserveTokens[0]]; uint256 reserve1Id = __reserveIds[_reserveTokens[1]]; (_reserveBalances[0], _reserveBalances[1]) = reserveBalances(reserve0Id, reserve1Id); uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); uint256 amount = fundSupplyAmount(totalSupply, _reserveBalances[_reserveTokenIndex], _reserveAmount); for (uint256 i = 0; i < 2; i++) _reserveAmounts[i] = fundCost(totalSupply, _reserveBalances[i], amount); return _reserveAmounts; } /** * @dev given the amount of one of the reserve tokens to add liquidity of, * returns the amount of pool tokens entitled for it * since an empty pool can be funded with any list of non-zero input amounts, * this function assumes that the pool is not empty (has already been funded) * * @param _reserveToken address of the reserve token * @param _reserveAmount amount of the reserve token * * @return the amount of pool tokens entitled */ function addLiquidityReturn(IERC20Token _reserveToken, uint256 _reserveAmount) public view returns (uint256) { uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); return fundSupplyAmount(totalSupply, reserveBalance(__reserveIds[_reserveToken]), _reserveAmount); } /** * @dev returns the amount of each reserve token entitled for a given amount of pool tokens * * @param _amount amount of pool tokens * @param _reserveTokens address of each reserve token * * @return the amount of each reserve token entitled for the given amount of pool tokens */ function removeLiquidityReturn(uint256 _amount, IERC20Token[] memory _reserveTokens) public view returns (uint256[] memory) { uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); return removeLiquidityReserveAmounts(_amount, _reserveTokens, totalSupply); } /** * @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 * this function rearranges the input arrays according to the converter's array of reserve tokens * * @param _reserveTokens array of reserve tokens * @param _reserveAmounts array of reserve amounts * @param _amount token amount * * @return true if the function has rearranged the input arrays; false otherwise */ function verifyLiquidityInput( IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _amount ) private view returns (bool) { require(_reserveAmounts[0] > 0 && _reserveAmounts[1] > 0 && _amount > 0, "ERR_ZERO_AMOUNT"); uint256 reserve0Id = __reserveIds[_reserveTokens[0]]; uint256 reserve1Id = __reserveIds[_reserveTokens[1]]; if (reserve0Id == 2 && reserve1Id == 1) { IERC20Token tempReserveToken = _reserveTokens[0]; _reserveTokens[0] = _reserveTokens[1]; _reserveTokens[1] = tempReserveToken; uint256 tempReserveAmount = _reserveAmounts[0]; _reserveAmounts[0] = _reserveAmounts[1]; _reserveAmounts[1] = tempReserveAmount; return true; } require(reserve0Id == 1 && reserve1Id == 2, "ERR_INVALID_RESERVE"); return false; } /** * @dev returns the amount of each reserve token entitled for a given amount of pool tokens * * @param _amount amount of pool tokens * @param _reserveTokens address of each reserve token * @param _totalSupply token total supply * * @return the amount of each reserve token entitled for the given amount of pool tokens */ function removeLiquidityReserveAmounts( uint256 _amount, IERC20Token[] memory _reserveTokens, uint256 _totalSupply ) private view returns (uint256[] memory) { uint256[] memory _reserveAmounts = new uint256[](2); uint256[] memory _reserveBalances = new uint256[](2); uint256 reserve0Id = __reserveIds[_reserveTokens[0]]; uint256 reserve1Id = __reserveIds[_reserveTokens[1]]; (_reserveBalances[0], _reserveBalances[1]) = reserveBalances(reserve0Id, reserve1Id); for (uint256 i = 0; i < 2; i++) _reserveAmounts[i] = liquidateReserveAmount(_totalSupply, _reserveBalances[i], _amount); return _reserveAmounts; } /** * @dev dispatches token rate update events for the reserve tokens and the pool token * * @param _sourceToken address of the source reserve token * @param _targetToken address of the target reserve token * @param _sourceBalance balance of the source reserve token * @param _targetBalance balance of the target reserve token */ function dispatchTokenRateUpdateEvents( IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _sourceBalance, uint256 _targetBalance ) private { // save a local copy of the pool token IDSToken poolToken = IDSToken(address(anchor)); // get the total supply of pool tokens uint256 poolTokenSupply = poolToken.totalSupply(); // dispatch token rate update event for the reserve tokens emit TokenRateUpdate(_sourceToken, _targetToken, _targetBalance, _sourceBalance); // dispatch token rate update events for the pool token emit TokenRateUpdate(poolToken, _sourceToken, _sourceBalance, poolTokenSupply); emit TokenRateUpdate(poolToken, _targetToken, _targetBalance, poolTokenSupply); } function encodeReserveBalance(uint256 _balance, uint256 _id) private pure returns (uint256) { assert(_balance <= MAX_UINT128 && (_id == 1 || _id == 2)); return _balance << ((_id - 1) * 128); } function decodeReserveBalance(uint256 _balances, uint256 _id) private pure returns (uint256) { assert(_id == 1 || _id == 2); return (_balances >> ((_id - 1) * 128)) & MAX_UINT128; } function encodeReserveBalances( uint256 _balance0, uint256 _id0, uint256 _balance1, uint256 _id1 ) private pure returns (uint256) { return encodeReserveBalance(_balance0, _id0) | encodeReserveBalance(_balance1, _id1); } function decodeReserveBalances( uint256 _balances, uint256 _id0, uint256 _id1 ) private pure returns (uint256, uint256) { return (decodeReserveBalance(_balances, _id0), decodeReserveBalance(_balances, _id1)); } function encodeAverageRateInfo( uint256 _averageRateT, uint256 _averageRateN, uint256 _averageRateD ) private pure returns (uint256) { assert(_averageRateT <= MAX_UINT32 && _averageRateN <= MAX_UINT112 && _averageRateD <= MAX_UINT112); return (_averageRateT << 224) | (_averageRateN << 112) | _averageRateD; } function decodeAverageRateT(uint256 _averageRateInfo) private pure returns (uint256) { return _averageRateInfo >> 224; } function decodeAverageRateN(uint256 _averageRateInfo) private pure returns (uint256) { return (_averageRateInfo >> 112) & MAX_UINT112; } function decodeAverageRateD(uint256 _averageRateInfo) private pure returns (uint256) { return _averageRateInfo & MAX_UINT112; } function crossReserveTargetAmount( uint256 _sourceReserveBalance, uint256 _targetReserveBalance, uint256 _amount ) private pure returns (uint256) { // validate input require(_sourceReserveBalance > 0 && _targetReserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE"); return _targetReserveBalance.mul(_amount) / _sourceReserveBalance.add(_amount); } function fundCost( uint256 _supply, uint256 _reserveBalance, uint256 _amount ) private pure returns (uint256) { // validate input require(_supply > 0, "ERR_INVALID_SUPPLY"); require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE"); // special case for 0 amount if (_amount == 0) { return 0; } return (_amount.mul(_reserveBalance) - 1) / _supply + 1; } function fundSupplyAmount( uint256 _supply, uint256 _reserveBalance, uint256 _amount ) private pure returns (uint256) { // validate input require(_supply > 0, "ERR_INVALID_SUPPLY"); require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE"); // special case for 0 amount if (_amount == 0) { return 0; } return _amount.mul(_supply) / _reserveBalance; } function liquidateReserveAmount( uint256 _supply, uint256 _reserveBalance, uint256 _amount ) private pure returns (uint256) { // validate input require(_supply > 0, "ERR_INVALID_SUPPLY"); require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE"); 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; } return _amount.mul(_reserveBalance) / _supply; } /** * @dev deprecated since version 28, backward compatibility - use only for earlier versions */ function token() public view override returns (IConverterAnchor) { return anchor; } /** * @dev deprecated, backward compatibility */ function transferTokenOwnership(address _newOwner) public override ownerOnly { transferAnchorOwnership(_newOwner); } /** * @dev deprecated, backward compatibility */ function acceptTokenOwnership() public override ownerOnly { acceptAnchorOwnership(); } /** * @dev deprecated, backward compatibility */ function connectors(IERC20Token _address) public view override returns ( uint256, uint32, bool, bool, bool ) { uint256 reserveId = __reserveIds[_address]; if (reserveId != 0) { return (reserveBalance(reserveId), PPM_RESOLUTION / 2, false, false, true); } return (0, 0, false, false, false); } /** * @dev deprecated, backward compatibility */ function connectorTokens(uint256 _index) public view override returns (IERC20Token) { return __reserveTokens[_index]; } /** * @dev deprecated, backward compatibility */ function connectorTokenCount() public view override returns (uint16) { return reserveTokenCount(); } /** * @dev deprecated, backward compatibility */ function getConnectorBalance(IERC20Token _connectorToken) public view override 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 2 of 4: DSToken
// File: solidity/contracts/token/interfaces/IERC20Token.sol // SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity 0.6.12; /* ERC20 Standard Token interface */ interface IERC20Token { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address _owner) external view returns (uint256); function allowance(address _owner, address _spender) external view returns (uint256); function transfer(address _to, uint256 _value) external returns (bool); function transferFrom(address _from, address _to, uint256 _value) external returns (bool); function approve(address _spender, uint256 _value) external returns (bool); } // File: solidity/contracts/utility/Utils.sol pragma solidity 0.6.12; /** * @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: solidity/contracts/utility/SafeMath.sol pragma solidity 0.6.12; /** * @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/token/ERC20Token.sol pragma solidity 0.6.12; /** * @dev ERC20 Standard Token implementation */ contract ERC20Token is IERC20Token, Utils { using SafeMath for uint256; string public override name; string public override symbol; uint8 public override decimals; uint256 public override totalSupply; mapping (address => uint256) public override balanceOf; mapping (address => mapping (address => uint256)) public override 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 memory _name, string memory _symbol, uint8 _decimals, uint256 _totalSupply) public { // validate input require(bytes(_name).length > 0, "ERR_INVALID_NAME"); require(bytes(_symbol).length > 0, "ERR_INVALID_SYMBOL"); name = _name; symbol = _symbol; decimals = _decimals; totalSupply = _totalSupply; balanceOf[msg.sender] = _totalSupply; } /** * @dev transfers tokens to a given address * 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 virtual override validAddress(_to) returns (bool) { balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value); balanceOf[_to] = balanceOf[_to].add(_value); emit Transfer(msg.sender, _to, _value); return true; } /** * @dev transfers tokens to a given address on behalf of another address * 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 virtual override validAddress(_from) validAddress(_to) returns (bool) { 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 allows another account/contract to transfers tokens on behalf of the caller * throws on any error rather then return a false flag to minimize user errors * * @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 virtual override validAddress(_spender) returns (bool) { allowance[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } } // File: solidity/contracts/utility/interfaces/IOwned.sol pragma solidity 0.6.12; /* Owned contract interface */ interface IOwned { // this function isn't since the compiler emits automatically generated getter functions as external function owner() external view returns (address); function transferOwnership(address _newOwner) external; function acceptOwnership() external; } // File: solidity/contracts/converter/interfaces/IConverterAnchor.sol pragma solidity 0.6.12; /* Converter Anchor interface */ interface IConverterAnchor is IOwned { } // File: solidity/contracts/token/interfaces/IDSToken.sol pragma solidity 0.6.12; /* DSToken interface */ interface IDSToken is IConverterAnchor, IERC20Token { function issue(address _to, uint256 _amount) external; function destroy(address _from, uint256 _amount) external; } // File: solidity/contracts/utility/Owned.sol pragma solidity 0.6.12; /** * @dev Provides support and utilities for contract ownership */ contract Owned is IOwned { address public override 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 override ownerOnly { require(_newOwner != owner, "ERR_SAME_OWNER"); newOwner = _newOwner; } /** * @dev used by a new owner to accept an ownership transfer */ function acceptOwnership() override public { require(msg.sender == newOwner, "ERR_ACCESS_DENIED"); emit OwnerUpdate(owner, newOwner); owner = newOwner; newOwner = address(0); } } // File: solidity/contracts/token/DSToken.sol pragma solidity 0.6.12; /** * @dev DSToken represents a token with dynamic supply. * The owner of the token can mint/burn tokens to/from any account. * */ contract DSToken is IDSToken, ERC20Token, Owned { using SafeMath for uint256; /** * @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 DSToken instance * * @param _name token name * @param _symbol token short symbol, minimum 1 character * @param _decimals for display purposes only */ constructor(string memory _name, string memory _symbol, uint8 _decimals) public ERC20Token(_name, _symbol, _decimals, 0) { } /** * @dev increases the token supply and sends the new tokens to the given 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 override ownerOnly validAddress(_to) notThis(_to) { totalSupply = totalSupply.add(_amount); balanceOf[_to] = balanceOf[_to].add(_amount); emit Issuance(_amount); emit Transfer(address(0), _to, _amount); } /** * @dev removes tokens from the given 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 override ownerOnly { balanceOf[_from] = balanceOf[_from].sub(_amount); totalSupply = totalSupply.sub(_amount); emit Transfer(_from, address(0), _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 override(IERC20Token, ERC20Token) returns (bool) { return super.transfer(_to, _value); } /** * @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 override(IERC20Token, ERC20Token) returns (bool) { return super.transferFrom(_from, _to, _value); } }
File 3 of 4: Uni
/** *Submitted for verification at Etherscan.io on 2020-09-15 */ pragma solidity ^0.5.16; pragma experimental ABIEncoderV2; // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol // Subject to the MIT license. /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, errorMessage); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot underflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction underflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot underflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, errorMessage); return c; } /** * @dev Returns the integer division of two unsigned integers. * Reverts on division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. * Reverts with custom message on division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } contract Uni { /// @notice EIP-20 token name for this token string public constant name = "Uniswap"; /// @notice EIP-20 token symbol for this token string public constant symbol = "UNI"; /// @notice EIP-20 token decimals for this token uint8 public constant decimals = 18; /// @notice Total number of tokens in circulation uint public totalSupply = 1_000_000_000e18; // 1 billion Uni /// @notice Address which may mint new tokens address public minter; /// @notice The timestamp after which minting may occur uint public mintingAllowedAfter; /// @notice Minimum time between mints uint32 public constant minimumTimeBetweenMints = 1 days * 365; /// @notice Cap on the percentage of totalSupply that can be minted at each mint uint8 public constant mintCap = 2; /// @notice Allowance amounts on behalf of others mapping (address => mapping (address => uint96)) internal allowances; /// @notice Official record of token balances for each account mapping (address => uint96) internal balances; /// @notice A record of each accounts delegate mapping (address => address) public delegates; /// @notice A checkpoint for marking number of votes from a given block struct Checkpoint { uint32 fromBlock; uint96 votes; } /// @notice A record of votes checkpoints for each account, by index mapping (address => mapping (uint32 => Checkpoint)) public checkpoints; /// @notice The number of checkpoints for each account mapping (address => uint32) public numCheckpoints; /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 typehash for the delegation struct used by the contract bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); /// @notice The EIP-712 typehash for the permit struct used by the contract bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); /// @notice A record of states for signing / validating signatures mapping (address => uint) public nonces; /// @notice An event thats emitted when the minter address is changed event MinterChanged(address minter, address newMinter); /// @notice An event thats emitted when an account changes its delegate event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); /// @notice An event thats emitted when a delegate account's vote balance changes event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance); /// @notice The standard EIP-20 transfer event event Transfer(address indexed from, address indexed to, uint256 amount); /// @notice The standard EIP-20 approval event event Approval(address indexed owner, address indexed spender, uint256 amount); /** * @notice Construct a new Uni token * @param account The initial account to grant all the tokens * @param minter_ The account with minting ability * @param mintingAllowedAfter_ The timestamp after which minting may occur */ constructor(address account, address minter_, uint mintingAllowedAfter_) public { require(mintingAllowedAfter_ >= block.timestamp, "Uni::constructor: minting can only begin after deployment"); balances[account] = uint96(totalSupply); emit Transfer(address(0), account, totalSupply); minter = minter_; emit MinterChanged(address(0), minter); mintingAllowedAfter = mintingAllowedAfter_; } /** * @notice Change the minter address * @param minter_ The address of the new minter */ function setMinter(address minter_) external { require(msg.sender == minter, "Uni::setMinter: only the minter can change the minter address"); emit MinterChanged(minter, minter_); minter = minter_; } /** * @notice Mint new tokens * @param dst The address of the destination account * @param rawAmount The number of tokens to be minted */ function mint(address dst, uint rawAmount) external { require(msg.sender == minter, "Uni::mint: only the minter can mint"); require(block.timestamp >= mintingAllowedAfter, "Uni::mint: minting not allowed yet"); require(dst != address(0), "Uni::mint: cannot transfer to the zero address"); // record the mint mintingAllowedAfter = SafeMath.add(block.timestamp, minimumTimeBetweenMints); // mint the amount uint96 amount = safe96(rawAmount, "Uni::mint: amount exceeds 96 bits"); require(amount <= SafeMath.div(SafeMath.mul(totalSupply, mintCap), 100), "Uni::mint: exceeded mint cap"); totalSupply = safe96(SafeMath.add(totalSupply, amount), "Uni::mint: totalSupply exceeds 96 bits"); // transfer the amount to the recipient balances[dst] = add96(balances[dst], amount, "Uni::mint: transfer amount overflows"); emit Transfer(address(0), dst, amount); // move delegates _moveDelegates(address(0), delegates[dst], amount); } /** * @notice Get the number of tokens `spender` is approved to spend on behalf of `account` * @param account The address of the account holding the funds * @param spender The address of the account spending the funds * @return The number of tokens approved */ function allowance(address account, address spender) external view returns (uint) { return allowances[account][spender]; } /** * @notice Approve `spender` to transfer up to `amount` from `src` * @dev This will overwrite the approval amount for `spender` * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) * @param spender The address of the account which may transfer tokens * @param rawAmount The number of tokens that are approved (2^256-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint rawAmount) external returns (bool) { uint96 amount; if (rawAmount == uint(-1)) { amount = uint96(-1); } else { amount = safe96(rawAmount, "Uni::approve: amount exceeds 96 bits"); } allowances[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } /** * @notice Triggers an approval from owner to spends * @param owner The address to approve from * @param spender The address to be approved * @param rawAmount The number of tokens that are approved (2^256-1 means infinite) * @param deadline The time at which to expire the signature * @param v The recovery byte of the signature * @param r Half of the ECDSA signature pair * @param s Half of the ECDSA signature pair */ function permit(address owner, address spender, uint rawAmount, uint deadline, uint8 v, bytes32 r, bytes32 s) external { uint96 amount; if (rawAmount == uint(-1)) { amount = uint96(-1); } else { amount = safe96(rawAmount, "Uni::permit: amount exceeds 96 bits"); } bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, rawAmount, nonces[owner]++, deadline)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require(signatory != address(0), "Uni::permit: invalid signature"); require(signatory == owner, "Uni::permit: unauthorized"); require(now <= deadline, "Uni::permit: signature expired"); allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @notice Get the number of tokens held by the `account` * @param account The address of the account to get the balance of * @return The number of tokens held */ function balanceOf(address account) external view returns (uint) { return balances[account]; } /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param rawAmount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint rawAmount) external returns (bool) { uint96 amount = safe96(rawAmount, "Uni::transfer: amount exceeds 96 bits"); _transferTokens(msg.sender, dst, amount); return true; } /** * @notice Transfer `amount` tokens from `src` to `dst` * @param src The address of the source account * @param dst The address of the destination account * @param rawAmount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint rawAmount) external returns (bool) { address spender = msg.sender; uint96 spenderAllowance = allowances[src][spender]; uint96 amount = safe96(rawAmount, "Uni::approve: amount exceeds 96 bits"); if (spender != src && spenderAllowance != uint96(-1)) { uint96 newAllowance = sub96(spenderAllowance, amount, "Uni::transferFrom: transfer amount exceeds spender allowance"); allowances[src][spender] = newAllowance; emit Approval(src, spender, newAllowance); } _transferTokens(src, dst, amount); return true; } /** * @notice Delegate votes from `msg.sender` to `delegatee` * @param delegatee The address to delegate votes to */ function delegate(address delegatee) public { return _delegate(msg.sender, delegatee); } /** * @notice Delegates votes from signatory to `delegatee` * @param delegatee The address to delegate votes to * @param nonce The contract state required to match the signature * @param expiry The time at which to expire the signature * @param v The recovery byte of the signature * @param r Half of the ECDSA signature pair * @param s Half of the ECDSA signature pair */ function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public { bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require(signatory != address(0), "Uni::delegateBySig: invalid signature"); require(nonce == nonces[signatory]++, "Uni::delegateBySig: invalid nonce"); require(now <= expiry, "Uni::delegateBySig: signature expired"); return _delegate(signatory, delegatee); } /** * @notice Gets the current votes balance for `account` * @param account The address to get votes balance * @return The number of current votes for `account` */ function getCurrentVotes(address account) external view returns (uint96) { uint32 nCheckpoints = numCheckpoints[account]; return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; } /** * @notice Determine the prior number of votes for an account as of a block number * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. * @param account The address of the account to check * @param blockNumber The block number to get the vote balance at * @return The number of votes the account had as of the given block */ function getPriorVotes(address account, uint blockNumber) public view returns (uint96) { require(blockNumber < block.number, "Uni::getPriorVotes: not yet determined"); uint32 nCheckpoints = numCheckpoints[account]; if (nCheckpoints == 0) { return 0; } // First check most recent balance if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { return checkpoints[account][nCheckpoints - 1].votes; } // Next check implicit zero balance if (checkpoints[account][0].fromBlock > blockNumber) { return 0; } uint32 lower = 0; uint32 upper = nCheckpoints - 1; while (upper > lower) { uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Checkpoint memory cp = checkpoints[account][center]; if (cp.fromBlock == blockNumber) { return cp.votes; } else if (cp.fromBlock < blockNumber) { lower = center; } else { upper = center - 1; } } return checkpoints[account][lower].votes; } function _delegate(address delegator, address delegatee) internal { address currentDelegate = delegates[delegator]; uint96 delegatorBalance = balances[delegator]; delegates[delegator] = delegatee; emit DelegateChanged(delegator, currentDelegate, delegatee); _moveDelegates(currentDelegate, delegatee, delegatorBalance); } function _transferTokens(address src, address dst, uint96 amount) internal { require(src != address(0), "Uni::_transferTokens: cannot transfer from the zero address"); require(dst != address(0), "Uni::_transferTokens: cannot transfer to the zero address"); balances[src] = sub96(balances[src], amount, "Uni::_transferTokens: transfer amount exceeds balance"); balances[dst] = add96(balances[dst], amount, "Uni::_transferTokens: transfer amount overflows"); emit Transfer(src, dst, amount); _moveDelegates(delegates[src], delegates[dst], amount); } function _moveDelegates(address srcRep, address dstRep, uint96 amount) internal { if (srcRep != dstRep && amount > 0) { if (srcRep != address(0)) { uint32 srcRepNum = numCheckpoints[srcRep]; uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; uint96 srcRepNew = sub96(srcRepOld, amount, "Uni::_moveVotes: vote amount underflows"); _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); } if (dstRep != address(0)) { uint32 dstRepNum = numCheckpoints[dstRep]; uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; uint96 dstRepNew = add96(dstRepOld, amount, "Uni::_moveVotes: vote amount overflows"); _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); } } } function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes) internal { uint32 blockNumber = safe32(block.number, "Uni::_writeCheckpoint: block number exceeds 32 bits"); if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; } else { checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); numCheckpoints[delegatee] = nCheckpoints + 1; } emit DelegateVotesChanged(delegatee, oldVotes, newVotes); } function safe32(uint n, string memory errorMessage) internal pure returns (uint32) { require(n < 2**32, errorMessage); return uint32(n); } function safe96(uint n, string memory errorMessage) internal pure returns (uint96) { require(n < 2**96, errorMessage); return uint96(n); } function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { uint96 c = a + b; require(c >= a, errorMessage); return c; } function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { require(b <= a, errorMessage); return a - b; } function getChainId() internal pure returns (uint) { uint256 chainId; assembly { chainId := chainid() } return chainId; } }
File 4 of 4: 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; } }