Transaction Hash:
Block:
15928597 at Nov-08-2022 11:28:35 PM +UTC
Transaction Fee:
0.004552398264530844 ETH
$11.17
Gas Used:
187,404 Gas / 24.291894861 Gwei
Emitted Events:
141 |
TetherToken.Transfer( from=[Sender] 0x99f1373dff5e819b7635f70d9862347e6d0a14aa, to=[Receiver] Vyper_contract, value=194734071000 )
|
142 |
TetherToken.Transfer( from=[Receiver] Vyper_contract, to=Vyper_contract, value=194734071000 )
|
143 |
FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7, 0x00000000000000000000000055b916ce078ea594c10a874ba67ecc3d62e29822, 0000000000000000000000000000000000000000000000000000002d5697cb11 )
|
144 |
Vyper_contract.TokenExchange( buyer=[Receiver] Vyper_contract, sold_id=2, tokens_sold=194734071000, bought_id=1, tokens_bought=194726316817 )
|
145 |
FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000055b916ce078ea594c10a874ba67ecc3d62e29822, 0x00000000000000000000000099f1373dff5e819b7635f70d9862347e6d0a14aa, 0000000000000000000000000000000000000000000000000000002d5697cb11 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x99F1373D...E6D0A14aa |
215.565356825463099788 Eth
Nonce: 30
|
215.560804427198568944 Eth
Nonce: 31
| 0.004552398264530844 | ||
0xA0b86991...E3606eB48 | |||||
0xbEbc4478...3032FF1C7 | (Curve.fi: DAI/USDC/USDT Pool) | ||||
0xdAC17F95...13D831ec7 | |||||
0xDAFEA492...692c98Bc5
Miner
| (Flashbots: Builder) | 1.446032731648910936 Eth | 1.446501241648910936 Eth | 0.00046851 |
Execution Trace
Vyper_contract.9db4f7aa( )
-
TetherToken.transferFrom( _from=0x99F1373DfF5E819B7635f70D9862347E6D0A14aa, _to=0x55B916Ce078eA594c10a874ba67eCc3d62e29822, _value=194734071000 )
-
Null: 0x000...004.00000000( )
Vyper_contract.exchange( i=2, j=1, dx=194734071000, min_dy=0 )
-
TetherToken.balanceOf( who=0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7 ) => ( 241261082148098 )
-
Null: 0x000...004.CALL( )
-
Null: 0x000...004.00000000( )
-
TetherToken.transferFrom( _from=0x55B916Ce078eA594c10a874ba67eCc3d62e29822, _to=0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7, _value=194734071000 )
-
Null: 0x000...004.00000000( )
-
TetherToken.balanceOf( who=0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7 ) => ( 241455816219098 )
-
Null: 0x000...004.CALL( )
-
Null: 0x000...004.00000000( )
FiatTokenProxy.a9059cbb( )
-
FiatTokenV2_1.transfer( to=0x55B916Ce078eA594c10a874ba67eCc3d62e29822, value=194726316817 ) => ( True )
-
-
Null: 0x000...004.00000000( )
-
FiatTokenProxy.70a08231( )
-
FiatTokenV2_1.balanceOf( account=0x55B916Ce078eA594c10a874ba67eCc3d62e29822 ) => ( 194726316817 )
-
FiatTokenProxy.a9059cbb( )
-
FiatTokenV2_1.transfer( to=0x99F1373DfF5E819B7635f70D9862347E6D0A14aa, value=194726316817 ) => ( True )
-
-
Null: 0x000...004.00000000( )
File 1 of 5: Vyper_contract
File 2 of 5: TetherToken
File 3 of 5: Vyper_contract
File 4 of 5: FiatTokenProxy
File 5 of 5: FiatTokenV2_1
# @version 0.3.1 """ @title Curve Registry Exchange Contract @license MIT @author Curve.Fi @notice Find pools, query exchange rates and perform swaps """ from vyper.interfaces import ERC20 interface AddressProvider: def admin() -> address: view def get_registry() -> address: view def get_address(idx: uint256) -> address: view interface Registry: def address_provider() -> address: view def get_A(_pool: address) -> uint256: view def get_fees(_pool: address) -> uint256[2]: view def get_coin_indices(_pool: address, _from: address, _to: address) -> (int128, int128, bool): view def get_n_coins(_pool: address) -> uint256[2]: view def get_balances(_pool: address) -> uint256[MAX_COINS]: view def get_underlying_balances(_pool: address) -> uint256[MAX_COINS]: view def get_rates(_pool: address) -> uint256[MAX_COINS]: view def get_decimals(_pool: address) -> uint256[MAX_COINS]: view def get_underlying_decimals(_pool: address) -> uint256[MAX_COINS]: view def find_pool_for_coins(_from: address, _to: address, i: uint256) -> address: view def get_lp_token(_pool: address) -> address: view def is_meta(_pool: address) -> bool: view interface CryptoRegistry: def get_coin_indices(_pool: address, _from: address, _to: address) -> (uint256, uint256): view interface CurvePool: def exchange(i: int128, j: int128, dx: uint256, min_dy: uint256): payable def exchange_underlying(i: int128, j: int128, dx: uint256, min_dy: uint256): payable def get_dy(i: int128, j: int128, amount: uint256) -> uint256: view def get_dy_underlying(i: int128, j: int128, amount: uint256) -> uint256: view def coins(i: uint256) -> address: view interface CryptoPool: def exchange(i: uint256, j: uint256, dx: uint256, min_dy: uint256): payable def exchange_underlying(i: uint256, j: uint256, dx: uint256, min_dy: uint256): payable def get_dy(i: uint256, j: uint256, amount: uint256) -> uint256: view def get_dy_underlying(i: uint256, j: uint256, amount: uint256) -> uint256: view interface CryptoPoolETH: def exchange(i: uint256, j: uint256, dx: uint256, min_dy: uint256, use_eth: bool): payable interface LendingBasePoolMetaZap: def exchange_underlying(pool: address, i: int128, j: int128, dx: uint256, min_dy: uint256): nonpayable interface CryptoMetaZap: def get_dy(pool: address, i: uint256, j: uint256, dx: uint256) -> uint256: view def exchange(pool: address, i: uint256, j: uint256, dx: uint256, min_dy: uint256, use_eth: bool): payable interface BasePool2Coins: def add_liquidity(amounts: uint256[2], min_mint_amount: uint256): nonpayable def calc_token_amount(amounts: uint256[2], is_deposit: bool) -> uint256: view def remove_liquidity_one_coin(token_amount: uint256, i: int128, min_amount: uint256): nonpayable def calc_withdraw_one_coin(token_amount: uint256, i: int128,) -> uint256: view interface BasePool3Coins: def add_liquidity(amounts: uint256[3], min_mint_amount: uint256): nonpayable def calc_token_amount(amounts: uint256[3], is_deposit: bool) -> uint256: view def remove_liquidity_one_coin(token_amount: uint256, i: int128, min_amount: uint256): nonpayable def calc_withdraw_one_coin(token_amount: uint256, i: int128,) -> uint256: view interface LendingBasePool3Coins: def add_liquidity(amounts: uint256[3], min_mint_amount: uint256, use_underlying: bool): nonpayable def calc_token_amount(amounts: uint256[3], is_deposit: bool) -> uint256: view def remove_liquidity_one_coin(token_amount: uint256, i: int128, min_amount: uint256, use_underlying: bool) -> uint256: nonpayable def calc_withdraw_one_coin(token_amount: uint256, i: int128,) -> uint256: view interface Calculator: def get_dx(n_coins: uint256, balances: uint256[MAX_COINS], amp: uint256, fee: uint256, rates: uint256[MAX_COINS], precisions: uint256[MAX_COINS], i: int128, j: int128, dx: uint256) -> uint256: view def get_dy(n_coins: uint256, balances: uint256[MAX_COINS], amp: uint256, fee: uint256, rates: uint256[MAX_COINS], precisions: uint256[MAX_COINS], i: int128, j: int128, dx: uint256[CALC_INPUT_SIZE]) -> uint256[CALC_INPUT_SIZE]: view event TokenExchange: buyer: indexed(address) receiver: indexed(address) pool: indexed(address) token_sold: address token_bought: address amount_sold: uint256 amount_bought: uint256 ETH_ADDRESS: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE WETH_ADDRESS: constant(address) = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 MAX_COINS: constant(int128) = 8 CALC_INPUT_SIZE: constant(uint256) = 100 EMPTY_POOL_LIST: constant(address[8]) = [ ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ] address_provider: AddressProvider registry: public(address) factory_registry: public(address) crypto_registry: public(address) default_calculator: public(address) is_killed: public(bool) pool_calculator: HashMap[address, address] is_approved: HashMap[address, HashMap[address, bool]] base_coins: HashMap[address, address[2]] @external def __init__(_address_provider: address, _calculator: address): """ @notice Constructor function """ self.address_provider = AddressProvider(_address_provider) self.registry = AddressProvider(_address_provider).get_registry() self.factory_registry = AddressProvider(_address_provider).get_address(3) self.crypto_registry = AddressProvider(_address_provider).get_address(5) self.default_calculator = _calculator @external @payable def __default__(): pass @view @internal def _get_exchange_amount( _registry: address, _pool: address, _from: address, _to: address, _amount: uint256 ) -> uint256: """ @notice Get the current number of coins received in an exchange @param _registry Registry address @param _pool Pool address @param _from Address of coin to be sent @param _to Address of coin to be received @param _amount Quantity of `_from` to be sent @return Quantity of `_to` to be received """ i: int128 = 0 j: int128 = 0 is_underlying: bool = False i, j, is_underlying = Registry(_registry).get_coin_indices(_pool, _from, _to) # dev: no market if is_underlying and (_registry == self.registry or Registry(_registry).is_meta(_pool)): return CurvePool(_pool).get_dy_underlying(i, j, _amount) return CurvePool(_pool).get_dy(i, j, _amount) @view @internal def _get_crypto_exchange_amount( _registry: address, _pool: address, _from: address, _to: address, _amount: uint256 ) -> uint256: """ @notice Get the current number of coins received in an exchange @param _registry Registry address @param _pool Pool address @param _from Address of coin to be sent @param _to Address of coin to be received @param _amount Quantity of `_from` to be sent @return Quantity of `_to` to be received """ i: uint256 = 0 j: uint256 = 0 i, j = CryptoRegistry(_registry).get_coin_indices(_pool, _from, _to) # dev: no market return CryptoPool(_pool).get_dy(i, j, _amount) @internal def _exchange( _registry: address, _pool: address, _from: address, _to: address, _amount: uint256, _expected: uint256, _sender: address, _receiver: address, ) -> uint256: assert not self.is_killed eth_amount: uint256 = 0 received_amount: uint256 = 0 i: int128 = 0 j: int128 = 0 is_underlying: bool = False i, j, is_underlying = Registry(_registry).get_coin_indices(_pool, _from, _to) # dev: no market if is_underlying and _registry == self.factory_registry: if Registry(_registry).is_meta(_pool): base_coins: address[2] = self.base_coins[_pool] if base_coins == empty(address[2]): base_coins = [CurvePool(_pool).coins(0), CurvePool(_pool).coins(1)] self.base_coins[_pool] = base_coins # we only need to use exchange underlying if the input or output is not in the base coins is_underlying = _from not in base_coins or _to not in base_coins else: # not a metapool so no underlying exchange method is_underlying = False # perform / verify input transfer if _from == ETH_ADDRESS: eth_amount = _amount else: response: Bytes[32] = raw_call( _from, _abi_encode( _sender, self, _amount, method_id=method_id("transferFrom(address,address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) # approve input token if _from != ETH_ADDRESS and not self.is_approved[_from][_pool]: response: Bytes[32] = raw_call( _from, _abi_encode( _pool, MAX_UINT256, method_id=method_id("approve(address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) self.is_approved[_from][_pool] = True # perform coin exchange if is_underlying: CurvePool(_pool).exchange_underlying(i, j, _amount, _expected, value=eth_amount) else: CurvePool(_pool).exchange(i, j, _amount, _expected, value=eth_amount) # perform output transfer if _to == ETH_ADDRESS: received_amount = self.balance raw_call(_receiver, b"", value=self.balance) else: received_amount = ERC20(_to).balanceOf(self) response: Bytes[32] = raw_call( _to, _abi_encode( _receiver, received_amount, method_id=method_id("transfer(address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) log TokenExchange(_sender, _receiver, _pool, _from, _to, _amount, received_amount) return received_amount @internal def _crypto_exchange( _pool: address, _from: address, _to: address, _amount: uint256, _expected: uint256, _sender: address, _receiver: address, ) -> uint256: assert not self.is_killed initial: address = _from target: address = _to if _from == ETH_ADDRESS: initial = WETH_ADDRESS if _to == ETH_ADDRESS: target = WETH_ADDRESS eth_amount: uint256 = 0 received_amount: uint256 = 0 i: uint256 = 0 j: uint256 = 0 i, j = CryptoRegistry(self.crypto_registry).get_coin_indices(_pool, initial, target) # dev: no market # perform / verify input transfer if _from == ETH_ADDRESS: eth_amount = _amount else: response: Bytes[32] = raw_call( _from, _abi_encode( _sender, self, _amount, method_id=method_id("transferFrom(address,address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) # approve input token if not self.is_approved[_from][_pool]: response: Bytes[32] = raw_call( _from, _abi_encode( _pool, MAX_UINT256, method_id=method_id("approve(address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) self.is_approved[_from][_pool] = True # perform coin exchange if ETH_ADDRESS in [_from, _to]: CryptoPoolETH(_pool).exchange(i, j, _amount, _expected, True, value=eth_amount) else: CryptoPool(_pool).exchange(i, j, _amount, _expected) # perform output transfer if _to == ETH_ADDRESS: received_amount = self.balance raw_call(_receiver, b"", value=self.balance) else: received_amount = ERC20(_to).balanceOf(self) response: Bytes[32] = raw_call( _to, _abi_encode( _receiver, received_amount, method_id=method_id("transfer(address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) log TokenExchange(_sender, _receiver, _pool, _from, _to, _amount, received_amount) return received_amount @payable @external @nonreentrant("lock") def exchange_with_best_rate( _from: address, _to: address, _amount: uint256, _expected: uint256, _receiver: address = msg.sender, ) -> uint256: """ @notice Perform an exchange using the pool that offers the best rate @dev Prior to calling this function, the caller must approve this contract to transfer `_amount` coins from `_from` Does NOT check rates in factory-deployed pools @param _from Address of coin being sent @param _to Address of coin being received @param _amount Quantity of `_from` being sent @param _expected Minimum quantity of `_from` received in order for the transaction to succeed @param _receiver Address to transfer the received tokens to @return uint256 Amount received """ if _from == ETH_ADDRESS: assert _amount == msg.value, "Incorrect ETH amount" else: assert msg.value == 0, "Incorrect ETH amount" registry: address = self.registry best_pool: address = ZERO_ADDRESS max_dy: uint256 = 0 for i in range(65536): pool: address = Registry(registry).find_pool_for_coins(_from, _to, i) if pool == ZERO_ADDRESS: break dy: uint256 = self._get_exchange_amount(registry, pool, _from, _to, _amount) if dy > max_dy: best_pool = pool max_dy = dy return self._exchange(registry, best_pool, _from, _to, _amount, _expected, msg.sender, _receiver) @payable @external @nonreentrant("lock") def exchange( _pool: address, _from: address, _to: address, _amount: uint256, _expected: uint256, _receiver: address = msg.sender, ) -> uint256: """ @notice Perform an exchange using a specific pool @dev Prior to calling this function, the caller must approve this contract to transfer `_amount` coins from `_from` Works for both regular and factory-deployed pools @param _pool Address of the pool to use for the swap @param _from Address of coin being sent @param _to Address of coin being received @param _amount Quantity of `_from` being sent @param _expected Minimum quantity of `_from` received in order for the transaction to succeed @param _receiver Address to transfer the received tokens to @return uint256 Amount received """ if _from == ETH_ADDRESS: assert _amount == msg.value, "Incorrect ETH amount" else: assert msg.value == 0, "Incorrect ETH amount" if Registry(self.crypto_registry).get_lp_token(_pool) != ZERO_ADDRESS: return self._crypto_exchange(_pool, _from, _to, _amount, _expected, msg.sender, _receiver) registry: address = self.registry if Registry(registry).get_lp_token(_pool) == ZERO_ADDRESS: registry = self.factory_registry return self._exchange(registry, _pool, _from, _to, _amount, _expected, msg.sender, _receiver) @external @payable def exchange_multiple( _route: address[9], _swap_params: uint256[3][4], _amount: uint256, _expected: uint256, _pools: address[4]=[ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS], _receiver: address=msg.sender ) -> uint256: """ @notice Perform up to four swaps in a single transaction @dev Routing and swap params must be determined off-chain. This functionality is designed for gas efficiency over ease-of-use. @param _route Array of [initial token, pool, token, pool, token, ...] The array is iterated until a pool address of 0x00, then the last given token is transferred to `_receiver` @param _swap_params Multidimensional array of [i, j, swap type] where i and j are the correct values for the n'th pool in `_route`. The swap type should be 1 for a stableswap `exchange`, 2 for stableswap `exchange_underlying`, 3 for a cryptoswap `exchange`, 4 for a cryptoswap `exchange_underlying`, 5 for factory metapools with lending base pool `exchange_underlying`, 6 for factory crypto-meta pools underlying exchange (`exchange` method in zap), 7-9 for underlying coin -> LP token "exchange" (actually `add_liquidity`), 10-11 for LP token -> underlying coin "exchange" (actually `remove_liquidity_one_coin`) @param _amount The amount of `_route[0]` token being sent. @param _expected The minimum amount received after the final swap. @param _pools Array of pools for swaps via zap contracts. This parameter is only needed for Polygon meta-factories underlying swaps. @param _receiver Address to transfer the final output token to. @return Received amount of the final output token """ input_token: address = _route[0] amount: uint256 = _amount output_token: address = ZERO_ADDRESS # validate / transfer initial token if input_token == ETH_ADDRESS: assert msg.value == amount else: assert msg.value == 0 response: Bytes[32] = raw_call( input_token, _abi_encode( msg.sender, self, amount, method_id=method_id("transferFrom(address,address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) for i in range(1,5): # 4 rounds of iteration to perform up to 4 swaps swap: address = _route[i*2-1] pool: address = _pools[i-1] # Only for Polygon meta-factories underlying swap (swap_type == 4) output_token = _route[i*2] params: uint256[3] = _swap_params[i-1] # i, j, swap type if not self.is_approved[input_token][swap]: # approve the pool to transfer the input token response: Bytes[32] = raw_call( input_token, _abi_encode( swap, MAX_UINT256, method_id=method_id("approve(address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) self.is_approved[input_token][swap] = True eth_amount: uint256 = 0 if input_token == ETH_ADDRESS: eth_amount = amount # perform the swap according to the swap type if params[2] == 1: CurvePool(swap).exchange(convert(params[0], int128), convert(params[1], int128), amount, 0, value=eth_amount) elif params[2] == 2: CurvePool(swap).exchange_underlying(convert(params[0], int128), convert(params[1], int128), amount, 0, value=eth_amount) elif params[2] == 3: if input_token == ETH_ADDRESS or output_token == ETH_ADDRESS: CryptoPoolETH(swap).exchange(params[0], params[1], amount, 0, True, value=eth_amount) else: CryptoPool(swap).exchange(params[0], params[1], amount, 0) elif params[2] == 4: CryptoPool(swap).exchange_underlying(params[0], params[1], amount, 0, value=eth_amount) elif params[2] == 5: LendingBasePoolMetaZap(swap).exchange_underlying(pool, convert(params[0], int128), convert(params[1], int128), amount, 0) elif params[2] == 6: use_eth: bool = input_token == ETH_ADDRESS or output_token == ETH_ADDRESS CryptoMetaZap(swap).exchange(pool, params[0], params[1], amount, 0, use_eth) elif params[2] == 7: _amounts: uint256[2] = [0, 0] _amounts[params[0]] = amount BasePool2Coins(swap).add_liquidity(_amounts, 0) elif params[2] == 8: _amounts: uint256[3] = [0, 0, 0] _amounts[params[0]] = amount BasePool3Coins(swap).add_liquidity(_amounts, 0) elif params[2] == 9: _amounts: uint256[3] = [0, 0, 0] _amounts[params[0]] = amount LendingBasePool3Coins(swap).add_liquidity(_amounts, 0, True) # example: aave on Polygon elif params[2] == 10: # The number of coins doesn't matter here BasePool3Coins(swap).remove_liquidity_one_coin(amount, convert(params[1], int128), 0) elif params[2] == 11: # The number of coins doesn't matter here LendingBasePool3Coins(swap).remove_liquidity_one_coin(amount, convert(params[1], int128), 0, True) # example: aave on Polygon else: raise "Bad swap type" # update the amount received if output_token == ETH_ADDRESS: amount = self.balance else: amount = ERC20(output_token).balanceOf(self) # sanity check, if the routing data is incorrect we will have a 0 balance and that is bad assert amount != 0, "Received nothing" # check if this was the last swap if i == 4 or _route[i*2+1] == ZERO_ADDRESS: break # if there is another swap, the output token becomes the input for the next round input_token = output_token # validate the final amount received assert amount >= _expected # transfer the final token to the receiver if output_token == ETH_ADDRESS: raw_call(_receiver, b"", value=amount) else: response: Bytes[32] = raw_call( output_token, _abi_encode( _receiver, amount, method_id=method_id("transfer(address,uint256)"), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) return amount @view @external def get_best_rate( _from: address, _to: address, _amount: uint256, _exclude_pools: address[8] = EMPTY_POOL_LIST ) -> (address, uint256): """ @notice Find the pool offering the best rate for a given swap. @dev Checks rates for regular and factory pools @param _from Address of coin being sent @param _to Address of coin being received @param _amount Quantity of `_from` being sent @param _exclude_pools A list of up to 8 addresses which shouldn't be returned @return Pool address, amount received """ best_pool: address = ZERO_ADDRESS max_dy: uint256 = 0 initial: address = _from target: address = _to if _from == ETH_ADDRESS: initial = WETH_ADDRESS if _to == ETH_ADDRESS: target = WETH_ADDRESS registry: address = self.crypto_registry for i in range(65536): pool: address = Registry(registry).find_pool_for_coins(initial, target, i) if pool == ZERO_ADDRESS: if i == 0: # we only check for stableswap pools if we did not find any crypto pools break return best_pool, max_dy elif pool in _exclude_pools: continue dy: uint256 = self._get_crypto_exchange_amount(registry, pool, initial, target, _amount) if dy > max_dy: best_pool = pool max_dy = dy registry = self.registry for i in range(65536): pool: address = Registry(registry).find_pool_for_coins(_from, _to, i) if pool == ZERO_ADDRESS: break elif pool in _exclude_pools: continue dy: uint256 = self._get_exchange_amount(registry, pool, _from, _to, _amount) if dy > max_dy: best_pool = pool max_dy = dy registry = self.factory_registry for i in range(65536): pool: address = Registry(registry).find_pool_for_coins(_from, _to, i) if pool == ZERO_ADDRESS: break elif pool in _exclude_pools: continue if ERC20(pool).totalSupply() == 0: # ignore pools without TVL as the call to `get_dy` will revert continue dy: uint256 = self._get_exchange_amount(registry, pool, _from, _to, _amount) if dy > max_dy: best_pool = pool max_dy = dy return best_pool, max_dy @view @external def get_exchange_amount(_pool: address, _from: address, _to: address, _amount: uint256) -> uint256: """ @notice Get the current number of coins received in an exchange @dev Works for both regular and factory-deployed pools @param _pool Pool address @param _from Address of coin to be sent @param _to Address of coin to be received @param _amount Quantity of `_from` to be sent @return Quantity of `_to` to be received """ registry: address = self.crypto_registry if Registry(registry).get_lp_token(_pool) != ZERO_ADDRESS: initial: address = _from target: address = _to if _from == ETH_ADDRESS: initial = WETH_ADDRESS if _to == ETH_ADDRESS: target = WETH_ADDRESS return self._get_crypto_exchange_amount(registry, _pool, initial, target, _amount) registry = self.registry if Registry(registry).get_lp_token(_pool) == ZERO_ADDRESS: registry = self.factory_registry return self._get_exchange_amount(registry, _pool, _from, _to, _amount) @view @external def get_input_amount(_pool: address, _from: address, _to: address, _amount: uint256) -> uint256: """ @notice Get the current number of coins required to receive the given amount in an exchange @param _pool Pool address @param _from Address of coin to be sent @param _to Address of coin to be received @param _amount Quantity of `_to` to be received @return Quantity of `_from` to be sent """ registry: address = self.registry i: int128 = 0 j: int128 = 0 is_underlying: bool = False i, j, is_underlying = Registry(registry).get_coin_indices(_pool, _from, _to) amp: uint256 = Registry(registry).get_A(_pool) fee: uint256 = Registry(registry).get_fees(_pool)[0] balances: uint256[MAX_COINS] = empty(uint256[MAX_COINS]) rates: uint256[MAX_COINS] = empty(uint256[MAX_COINS]) decimals: uint256[MAX_COINS] = empty(uint256[MAX_COINS]) n_coins: uint256 = Registry(registry).get_n_coins(_pool)[convert(is_underlying, uint256)] if is_underlying: balances = Registry(registry).get_underlying_balances(_pool) decimals = Registry(registry).get_underlying_decimals(_pool) for x in range(MAX_COINS): if x == n_coins: break rates[x] = 10**18 else: balances = Registry(registry).get_balances(_pool) decimals = Registry(registry).get_decimals(_pool) rates = Registry(registry).get_rates(_pool) for x in range(MAX_COINS): if x == n_coins: break decimals[x] = 10 ** (18 - decimals[x]) calculator: address = self.pool_calculator[_pool] if calculator == ZERO_ADDRESS: calculator = self.default_calculator return Calculator(calculator).get_dx(n_coins, balances, amp, fee, rates, decimals, i, j, _amount) @view @external def get_exchange_amounts( _pool: address, _from: address, _to: address, _amounts: uint256[CALC_INPUT_SIZE] ) -> uint256[CALC_INPUT_SIZE]: """ @notice Get the current number of coins required to receive the given amount in an exchange @param _pool Pool address @param _from Address of coin to be sent @param _to Address of coin to be received @param _amounts Quantity of `_to` to be received @return Quantity of `_from` to be sent """ registry: address = self.registry i: int128 = 0 j: int128 = 0 is_underlying: bool = False balances: uint256[MAX_COINS] = empty(uint256[MAX_COINS]) rates: uint256[MAX_COINS] = empty(uint256[MAX_COINS]) decimals: uint256[MAX_COINS] = empty(uint256[MAX_COINS]) amp: uint256 = Registry(registry).get_A(_pool) fee: uint256 = Registry(registry).get_fees(_pool)[0] i, j, is_underlying = Registry(registry).get_coin_indices(_pool, _from, _to) n_coins: uint256 = Registry(registry).get_n_coins(_pool)[convert(is_underlying, uint256)] if is_underlying: balances = Registry(registry).get_underlying_balances(_pool) decimals = Registry(registry).get_underlying_decimals(_pool) for x in range(MAX_COINS): if x == n_coins: break rates[x] = 10**18 else: balances = Registry(registry).get_balances(_pool) decimals = Registry(registry).get_decimals(_pool) rates = Registry(registry).get_rates(_pool) for x in range(MAX_COINS): if x == n_coins: break decimals[x] = 10 ** (18 - decimals[x]) calculator: address = self.pool_calculator[_pool] if calculator == ZERO_ADDRESS: calculator = self.default_calculator return Calculator(calculator).get_dy(n_coins, balances, amp, fee, rates, decimals, i, j, _amounts) @view @external def get_exchange_multiple_amount( _route: address[9], _swap_params: uint256[3][4], _amount: uint256, _pools: address[4]=[ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS] ) -> uint256: """ @notice Get the current number the final output tokens received in an exchange @dev Routing and swap params must be determined off-chain. This functionality is designed for gas efficiency over ease-of-use. @param _route Array of [initial token, pool, token, pool, token, ...] The array is iterated until a pool address of 0x00, then the last given token is transferred to `_receiver` @param _swap_params Multidimensional array of [i, j, swap type] where i and j are the correct values for the n'th pool in `_route`. The swap type should be 1 for a stableswap `exchange`, 2 for stableswap `exchange_underlying`, 3 for a cryptoswap `exchange`, 4 for a cryptoswap `exchange_underlying`, 5 for factory metapools with lending base pool `exchange_underlying`, 6 for factory crypto-meta pools underlying exchange (`exchange` method in zap), 7-9 for underlying coin -> LP token "exchange" (actually `add_liquidity`), 10-11 for LP token -> underlying coin "exchange" (actually `remove_liquidity_one_coin`) @param _amount The amount of `_route[0]` token to be sent. @param _pools Array of pools for swaps via zap contracts. This parameter is only needed for Polygon meta-factories underlying swaps. @return Expected amount of the final output token """ amount: uint256 = _amount for i in range(1,5): # 4 rounds of iteration to perform up to 4 swaps swap: address = _route[i*2-1] pool: address = _pools[i-1] # Only for Polygon meta-factories underlying swap (swap_type == 4) params: uint256[3] = _swap_params[i-1] # i, j, swap type # Calc output amount according to the swap type if params[2] == 1: amount = CurvePool(swap).get_dy(convert(params[0], int128), convert(params[1], int128), amount) elif params[2] == 2: amount = CurvePool(swap).get_dy_underlying(convert(params[0], int128), convert(params[1], int128), amount) elif params[2] == 3: amount = CryptoPool(swap).get_dy(params[0], params[1], amount) elif params[2] == 4: amount = CryptoPool(swap).get_dy_underlying(params[0], params[1], amount) elif params[2] == 5: amount = CurvePool(pool).get_dy_underlying(convert(params[0], int128), convert(params[1], int128), amount) elif params[2] == 6: amount = CryptoMetaZap(swap).get_dy(pool, params[0], params[1], amount) elif params[2] == 7: _amounts: uint256[2] = [0, 0] _amounts[params[0]] = amount amount = BasePool2Coins(swap).calc_token_amount(_amounts, True) elif params[2] in [8, 9]: _amounts: uint256[3] = [0, 0, 0] _amounts[params[0]] = amount amount = BasePool3Coins(swap).calc_token_amount(_amounts, True) elif params[2] in [10, 11]: # The number of coins doesn't matter here amount = BasePool3Coins(swap).calc_withdraw_one_coin(amount, convert(params[1], int128)) else: raise "Bad swap type" # check if this was the last swap if i == 4 or _route[i*2+1] == ZERO_ADDRESS: break return amount @view @external def get_calculator(_pool: address) -> address: """ @notice Set calculator contract @dev Used to calculate `get_dy` for a pool @param _pool Pool address @return `CurveCalc` address """ calculator: address = self.pool_calculator[_pool] if calculator == ZERO_ADDRESS: return self.default_calculator else: return calculator @external def update_registry_address() -> bool: """ @notice Update registry address @dev The registry address is kept in storage to reduce gas costs. If a new registry is deployed this function should be called to update the local address from the address provider. @return bool success """ address_provider: address = self.address_provider.address self.registry = AddressProvider(address_provider).get_registry() self.factory_registry = AddressProvider(address_provider).get_address(3) self.crypto_registry = AddressProvider(address_provider).get_address(5) return True @external def set_calculator(_pool: address, _calculator: address) -> bool: """ @notice Set calculator contract @dev Used to calculate `get_dy` for a pool @param _pool Pool address @param _calculator `CurveCalc` address @return bool success """ assert msg.sender == self.address_provider.admin() # dev: admin-only function self.pool_calculator[_pool] = _calculator return True @external def set_default_calculator(_calculator: address) -> bool: """ @notice Set default calculator contract @dev Used to calculate `get_dy` for a pool @param _calculator `CurveCalc` address @return bool success """ assert msg.sender == self.address_provider.admin() # dev: admin-only function self.default_calculator = _calculator return True @external def claim_balance(_token: address) -> bool: """ @notice Transfer an ERC20 or ETH balance held by this contract @dev The entire balance is transferred to the owner @param _token Token address @return bool success """ assert msg.sender == self.address_provider.admin() # dev: admin-only function if _token == ETH_ADDRESS: raw_call(msg.sender, b"", value=self.balance) else: amount: uint256 = ERC20(_token).balanceOf(self) response: Bytes[32] = raw_call( _token, concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(amount, bytes32), ), max_outsize=32, ) if len(response) != 0: assert convert(response, bool) return True @external def set_killed(_is_killed: bool) -> bool: """ @notice Kill or unkill the contract @param _is_killed Killed status of the contract @return bool success """ assert msg.sender == self.address_provider.admin() # dev: admin-only function self.is_killed = _is_killed return True
File 2 of 5: TetherToken
pragma solidity ^0.4.17; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ function Ownable() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { if (newOwner != address(0)) { owner = newOwner; } } } /** * @title ERC20Basic * @dev Simpler version of ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ contract ERC20Basic { uint public _totalSupply; function totalSupply() public constant returns (uint); function balanceOf(address who) public constant returns (uint); function transfer(address to, uint value) public; event Transfer(address indexed from, address indexed to, uint value); } /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ contract ERC20 is ERC20Basic { function allowance(address owner, address spender) public constant returns (uint); function transferFrom(address from, address to, uint value) public; function approve(address spender, uint value) public; event Approval(address indexed owner, address indexed spender, uint value); } /** * @title Basic token * @dev Basic version of StandardToken, with no allowances. */ contract BasicToken is Ownable, ERC20Basic { using SafeMath for uint; mapping(address => uint) public balances; // additional variables for use if transaction fees ever became necessary uint public basisPointsRate = 0; uint public maximumFee = 0; /** * @dev Fix for the ERC20 short address attack. */ modifier onlyPayloadSize(uint size) { require(!(msg.data.length < size + 4)); _; } /** * @dev transfer token for a specified address * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) { uint fee = (_value.mul(basisPointsRate)).div(10000); if (fee > maximumFee) { fee = maximumFee; } uint sendAmount = _value.sub(fee); balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(sendAmount); if (fee > 0) { balances[owner] = balances[owner].add(fee); Transfer(msg.sender, owner, fee); } Transfer(msg.sender, _to, sendAmount); } /** * @dev Gets the balance of the specified address. * @param _owner The address to query the the balance of. * @return An uint representing the amount owned by the passed address. */ function balanceOf(address _owner) public constant returns (uint balance) { return balances[_owner]; } } /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * @dev https://github.com/ethereum/EIPs/issues/20 * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol */ contract StandardToken is BasicToken, ERC20 { mapping (address => mapping (address => uint)) public allowed; uint public constant MAX_UINT = 2**256 - 1; /** * @dev Transfer tokens from one address to another * @param _from address The address which you want to send tokens from * @param _to address The address which you want to transfer to * @param _value uint the amount of tokens to be transferred */ function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) { var _allowance = allowed[_from][msg.sender]; // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met // if (_value > _allowance) throw; uint fee = (_value.mul(basisPointsRate)).div(10000); if (fee > maximumFee) { fee = maximumFee; } if (_allowance < MAX_UINT) { allowed[_from][msg.sender] = _allowance.sub(_value); } uint sendAmount = _value.sub(fee); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(sendAmount); if (fee > 0) { balances[owner] = balances[owner].add(fee); Transfer(_from, owner, fee); } Transfer(_from, _to, sendAmount); } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * @param _spender The address which will spend the funds. * @param _value The amount of tokens to be spent. */ function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) { // To change the approve amount you first have to reduce the addresses` // allowance to zero by calling `approve(_spender, 0)` if it is not // already 0 to mitigate the race condition described here: // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 require(!((_value != 0) && (allowed[msg.sender][_spender] != 0))); allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); } /** * @dev Function to check the amount of tokens than an owner allowed to a spender. * @param _owner address The address which owns the funds. * @param _spender address The address which will spend the funds. * @return A uint specifying the amount of tokens still available for the spender. */ function allowance(address _owner, address _spender) public constant returns (uint remaining) { return allowed[_owner][_spender]; } } /** * @title Pausable * @dev Base contract which allows children to implement an emergency stop mechanism. */ contract Pausable is Ownable { event Pause(); event Unpause(); bool public paused = false; /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() onlyOwner whenNotPaused public { paused = true; Pause(); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() onlyOwner whenPaused public { paused = false; Unpause(); } } contract BlackList is Ownable, BasicToken { /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) /////// function getBlackListStatus(address _maker) external constant returns (bool) { return isBlackListed[_maker]; } function getOwner() external constant returns (address) { return owner; } mapping (address => bool) public isBlackListed; function addBlackList (address _evilUser) public onlyOwner { isBlackListed[_evilUser] = true; AddedBlackList(_evilUser); } function removeBlackList (address _clearedUser) public onlyOwner { isBlackListed[_clearedUser] = false; RemovedBlackList(_clearedUser); } function destroyBlackFunds (address _blackListedUser) public onlyOwner { require(isBlackListed[_blackListedUser]); uint dirtyFunds = balanceOf(_blackListedUser); balances[_blackListedUser] = 0; _totalSupply -= dirtyFunds; DestroyedBlackFunds(_blackListedUser, dirtyFunds); } event DestroyedBlackFunds(address _blackListedUser, uint _balance); event AddedBlackList(address _user); event RemovedBlackList(address _user); } contract UpgradedStandardToken is StandardToken{ // those methods are called by the legacy contract // and they must ensure msg.sender to be the contract address function transferByLegacy(address from, address to, uint value) public; function transferFromByLegacy(address sender, address from, address spender, uint value) public; function approveByLegacy(address from, address spender, uint value) public; } contract TetherToken is Pausable, StandardToken, BlackList { string public name; string public symbol; uint public decimals; address public upgradedAddress; bool public deprecated; // The contract can be initialized with a number of tokens // All the tokens are deposited to the owner address // // @param _balance Initial supply of the contract // @param _name Token Name // @param _symbol Token symbol // @param _decimals Token decimals function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public { _totalSupply = _initialSupply; name = _name; symbol = _symbol; decimals = _decimals; balances[owner] = _initialSupply; deprecated = false; } // Forward ERC20 methods to upgraded contract if this one is deprecated function transfer(address _to, uint _value) public whenNotPaused { require(!isBlackListed[msg.sender]); if (deprecated) { return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value); } else { return super.transfer(_to, _value); } } // Forward ERC20 methods to upgraded contract if this one is deprecated function transferFrom(address _from, address _to, uint _value) public whenNotPaused { require(!isBlackListed[_from]); if (deprecated) { return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value); } else { return super.transferFrom(_from, _to, _value); } } // Forward ERC20 methods to upgraded contract if this one is deprecated function balanceOf(address who) public constant returns (uint) { if (deprecated) { return UpgradedStandardToken(upgradedAddress).balanceOf(who); } else { return super.balanceOf(who); } } // Forward ERC20 methods to upgraded contract if this one is deprecated function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) { if (deprecated) { return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value); } else { return super.approve(_spender, _value); } } // Forward ERC20 methods to upgraded contract if this one is deprecated function allowance(address _owner, address _spender) public constant returns (uint remaining) { if (deprecated) { return StandardToken(upgradedAddress).allowance(_owner, _spender); } else { return super.allowance(_owner, _spender); } } // deprecate current contract in favour of a new one function deprecate(address _upgradedAddress) public onlyOwner { deprecated = true; upgradedAddress = _upgradedAddress; Deprecate(_upgradedAddress); } // deprecate current contract if favour of a new one function totalSupply() public constant returns (uint) { if (deprecated) { return StandardToken(upgradedAddress).totalSupply(); } else { return _totalSupply; } } // Issue a new amount of tokens // these tokens are deposited into the owner address // // @param _amount Number of tokens to be issued function issue(uint amount) public onlyOwner { require(_totalSupply + amount > _totalSupply); require(balances[owner] + amount > balances[owner]); balances[owner] += amount; _totalSupply += amount; Issue(amount); } // Redeem tokens. // These tokens are withdrawn from the owner address // if the balance must be enough to cover the redeem // or the call will fail. // @param _amount Number of tokens to be issued function redeem(uint amount) public onlyOwner { require(_totalSupply >= amount); require(balances[owner] >= amount); _totalSupply -= amount; balances[owner] -= amount; Redeem(amount); } function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner { // Ensure transparency by hardcoding limit beyond which fees can never be added require(newBasisPoints < 20); require(newMaxFee < 50); basisPointsRate = newBasisPoints; maximumFee = newMaxFee.mul(10**decimals); Params(basisPointsRate, maximumFee); } // Called when new token are issued event Issue(uint amount); // Called when tokens are redeemed event Redeem(uint amount); // Called when contract is deprecated event Deprecate(address newAddress); // Called if contract ever adds fees event Params(uint feeBasisPoints, uint maxFee); }
File 3 of 5: Vyper_contract
# @version 0.2.4 # (c) Curve.Fi, 2020 # Pool for DAI/USDC/USDT from vyper.interfaces import ERC20 interface CurveToken: def totalSupply() -> uint256: view def mint(_to: address, _value: uint256) -> bool: nonpayable def burnFrom(_to: address, _value: uint256) -> bool: nonpayable # Events event TokenExchange: buyer: indexed(address) sold_id: int128 tokens_sold: uint256 bought_id: int128 tokens_bought: uint256 event AddLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] fees: uint256[N_COINS] invariant: uint256 token_supply: uint256 event RemoveLiquidity: provider: indexed(address) token_amounts: uint256[N_COINS] fees: uint256[N_COINS] token_supply: uint256 event RemoveLiquidityOne: provider: indexed(address) token_amount: uint256 coin_amount: uint256 event RemoveLiquidityImbalance: provider: indexed(address) token_amounts: uint256[N_COINS] fees: uint256[N_COINS] invariant: uint256 token_supply: uint256 event CommitNewAdmin: deadline: indexed(uint256) admin: indexed(address) event NewAdmin: admin: indexed(address) event CommitNewFee: deadline: indexed(uint256) fee: uint256 admin_fee: uint256 event NewFee: fee: uint256 admin_fee: uint256 event RampA: old_A: uint256 new_A: uint256 initial_time: uint256 future_time: uint256 event StopRampA: A: uint256 t: uint256 # This can (and needs to) be changed at compile time N_COINS: constant(int128) = 3 # <- change FEE_DENOMINATOR: constant(uint256) = 10 ** 10 LENDING_PRECISION: constant(uint256) = 10 ** 18 PRECISION: constant(uint256) = 10 ** 18 # The precision to convert to PRECISION_MUL: constant(uint256[N_COINS]) = [1, 1000000000000, 1000000000000] RATES: constant(uint256[N_COINS]) = [1000000000000000000, 1000000000000000000000000000000, 1000000000000000000000000000000] FEE_INDEX: constant(int128) = 2 # Which coin may potentially have fees (USDT) MAX_ADMIN_FEE: constant(uint256) = 10 * 10 ** 9 MAX_FEE: constant(uint256) = 5 * 10 ** 9 MAX_A: constant(uint256) = 10 ** 6 MAX_A_CHANGE: constant(uint256) = 10 ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400 MIN_RAMP_TIME: constant(uint256) = 86400 coins: public(address[N_COINS]) balances: public(uint256[N_COINS]) fee: public(uint256) # fee * 1e10 admin_fee: public(uint256) # admin_fee * 1e10 owner: public(address) token: CurveToken initial_A: public(uint256) future_A: public(uint256) initial_A_time: public(uint256) future_A_time: public(uint256) admin_actions_deadline: public(uint256) transfer_ownership_deadline: public(uint256) future_fee: public(uint256) future_admin_fee: public(uint256) future_owner: public(address) is_killed: bool kill_deadline: uint256 KILL_DEADLINE_DT: constant(uint256) = 2 * 30 * 86400 @external def __init__( _owner: address, _coins: address[N_COINS], _pool_token: address, _A: uint256, _fee: uint256, _admin_fee: uint256 ): """ @notice Contract constructor @param _owner Contract owner address @param _coins Addresses of ERC20 conracts of coins @param _pool_token Address of the token representing LP share @param _A Amplification coefficient multiplied by n * (n - 1) @param _fee Fee to charge for exchanges @param _admin_fee Admin fee """ for i in range(N_COINS): assert _coins[i] != ZERO_ADDRESS self.coins = _coins self.initial_A = _A self.future_A = _A self.fee = _fee self.admin_fee = _admin_fee self.owner = _owner self.kill_deadline = block.timestamp + KILL_DEADLINE_DT self.token = CurveToken(_pool_token) @view @internal def _A() -> uint256: """ Handle ramping A up or down """ t1: uint256 = self.future_A_time A1: uint256 = self.future_A if block.timestamp < t1: A0: uint256 = self.initial_A t0: uint256 = self.initial_A_time # Expressions in uint256 cannot have negative numbers, thus "if" if A1 > A0: return A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0) else: return A0 - (A0 - A1) * (block.timestamp - t0) / (t1 - t0) else: # when t1 == 0 or block.timestamp >= t1 return A1 @view @external def A() -> uint256: return self._A() @view @internal def _xp() -> uint256[N_COINS]: result: uint256[N_COINS] = RATES for i in range(N_COINS): result[i] = result[i] * self.balances[i] / LENDING_PRECISION return result @pure @internal def _xp_mem(_balances: uint256[N_COINS]) -> uint256[N_COINS]: result: uint256[N_COINS] = RATES for i in range(N_COINS): result[i] = result[i] * _balances[i] / PRECISION return result @pure @internal def get_D(xp: uint256[N_COINS], amp: uint256) -> uint256: S: uint256 = 0 for _x in xp: S += _x if S == 0: return 0 Dprev: uint256 = 0 D: uint256 = S Ann: uint256 = amp * N_COINS for _i in range(255): D_P: uint256 = D for _x in xp: D_P = D_P * D / (_x * N_COINS) # If division by 0, this will be borked: only withdrawal will work. And that is good Dprev = D D = (Ann * S + D_P * N_COINS) * D / ((Ann - 1) * D + (N_COINS + 1) * D_P) # Equality with the precision of 1 if D > Dprev: if D - Dprev <= 1: break else: if Dprev - D <= 1: break return D @view @internal def get_D_mem(_balances: uint256[N_COINS], amp: uint256) -> uint256: return self.get_D(self._xp_mem(_balances), amp) @view @external def get_virtual_price() -> uint256: """ Returns portfolio virtual price (for calculating profit) scaled up by 1e18 """ D: uint256 = self.get_D(self._xp(), self._A()) # D is in the units similar to DAI (e.g. converted to precision 1e18) # When balanced, D = n * x_u - total virtual value of the portfolio token_supply: uint256 = self.token.totalSupply() return D * PRECISION / token_supply @view @external def calc_token_amount(amounts: uint256[N_COINS], deposit: bool) -> uint256: """ Simplified method to calculate addition or reduction in token supply at deposit or withdrawal without taking fees into account (but looking at slippage). Needed to prevent front-running, not for precise calculations! """ _balances: uint256[N_COINS] = self.balances amp: uint256 = self._A() D0: uint256 = self.get_D_mem(_balances, amp) for i in range(N_COINS): if deposit: _balances[i] += amounts[i] else: _balances[i] -= amounts[i] D1: uint256 = self.get_D_mem(_balances, amp) token_amount: uint256 = self.token.totalSupply() diff: uint256 = 0 if deposit: diff = D1 - D0 else: diff = D0 - D1 return diff * token_amount / D0 @external @nonreentrant('lock') def add_liquidity(amounts: uint256[N_COINS], min_mint_amount: uint256): assert not self.is_killed # dev: is killed fees: uint256[N_COINS] = empty(uint256[N_COINS]) _fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1)) _admin_fee: uint256 = self.admin_fee amp: uint256 = self._A() token_supply: uint256 = self.token.totalSupply() # Initial invariant D0: uint256 = 0 old_balances: uint256[N_COINS] = self.balances if token_supply > 0: D0 = self.get_D_mem(old_balances, amp) new_balances: uint256[N_COINS] = old_balances for i in range(N_COINS): in_amount: uint256 = amounts[i] if token_supply == 0: assert in_amount > 0 # dev: initial deposit requires all coins in_coin: address = self.coins[i] # Take coins from the sender if in_amount > 0: if i == FEE_INDEX: in_amount = ERC20(in_coin).balanceOf(self) # "safeTransferFrom" which works for ERC20s which return bool or not _response: Bytes[32] = raw_call( in_coin, concat( method_id("transferFrom(address,address,uint256)"), convert(msg.sender, bytes32), convert(self, bytes32), convert(amounts[i], bytes32), ), max_outsize=32, ) # dev: failed transfer if len(_response) > 0: assert convert(_response, bool) # dev: failed transfer if i == FEE_INDEX: in_amount = ERC20(in_coin).balanceOf(self) - in_amount new_balances[i] = old_balances[i] + in_amount # Invariant after change D1: uint256 = self.get_D_mem(new_balances, amp) assert D1 > D0 # We need to recalculate the invariant accounting for fees # to calculate fair user's share D2: uint256 = D1 if token_supply > 0: # Only account for fees if we are not the first to deposit for i in range(N_COINS): ideal_balance: uint256 = D1 * old_balances[i] / D0 difference: uint256 = 0 if ideal_balance > new_balances[i]: difference = ideal_balance - new_balances[i] else: difference = new_balances[i] - ideal_balance fees[i] = _fee * difference / FEE_DENOMINATOR self.balances[i] = new_balances[i] - (fees[i] * _admin_fee / FEE_DENOMINATOR) new_balances[i] -= fees[i] D2 = self.get_D_mem(new_balances, amp) else: self.balances = new_balances # Calculate, how much pool tokens to mint mint_amount: uint256 = 0 if token_supply == 0: mint_amount = D1 # Take the dust if there was any else: mint_amount = token_supply * (D2 - D0) / D0 assert mint_amount >= min_mint_amount, "Slippage screwed you" # Mint pool tokens self.token.mint(msg.sender, mint_amount) log AddLiquidity(msg.sender, amounts, fees, D1, token_supply + mint_amount) @view @internal def get_y(i: int128, j: int128, x: uint256, xp_: uint256[N_COINS]) -> uint256: # x in the input is converted to the same price/precision assert i != j # dev: same coin assert j >= 0 # dev: j below zero assert j < N_COINS # dev: j above N_COINS # should be unreachable, but good for safety assert i >= 0 assert i < N_COINS amp: uint256 = self._A() D: uint256 = self.get_D(xp_, amp) c: uint256 = D S_: uint256 = 0 Ann: uint256 = amp * N_COINS _x: uint256 = 0 for _i in range(N_COINS): if _i == i: _x = x elif _i != j: _x = xp_[_i] else: continue S_ += _x c = c * D / (_x * N_COINS) c = c * D / (Ann * N_COINS) b: uint256 = S_ + D / Ann # - D y_prev: uint256 = 0 y: uint256 = D for _i in range(255): y_prev = y y = (y*y + c) / (2 * y + b - D) # Equality with the precision of 1 if y > y_prev: if y - y_prev <= 1: break else: if y_prev - y <= 1: break return y @view @external def get_dy(i: int128, j: int128, dx: uint256) -> uint256: # dx and dy in c-units rates: uint256[N_COINS] = RATES xp: uint256[N_COINS] = self._xp() x: uint256 = xp[i] + (dx * rates[i] / PRECISION) y: uint256 = self.get_y(i, j, x, xp) dy: uint256 = (xp[j] - y - 1) * PRECISION / rates[j] _fee: uint256 = self.fee * dy / FEE_DENOMINATOR return dy - _fee @view @external def get_dy_underlying(i: int128, j: int128, dx: uint256) -> uint256: # dx and dy in underlying units xp: uint256[N_COINS] = self._xp() precisions: uint256[N_COINS] = PRECISION_MUL x: uint256 = xp[i] + dx * precisions[i] y: uint256 = self.get_y(i, j, x, xp) dy: uint256 = (xp[j] - y - 1) / precisions[j] _fee: uint256 = self.fee * dy / FEE_DENOMINATOR return dy - _fee @external @nonreentrant('lock') def exchange(i: int128, j: int128, dx: uint256, min_dy: uint256): assert not self.is_killed # dev: is killed rates: uint256[N_COINS] = RATES old_balances: uint256[N_COINS] = self.balances xp: uint256[N_COINS] = self._xp_mem(old_balances) # Handling an unexpected charge of a fee on transfer (USDT, PAXG) dx_w_fee: uint256 = dx input_coin: address = self.coins[i] if i == FEE_INDEX: dx_w_fee = ERC20(input_coin).balanceOf(self) # "safeTransferFrom" which works for ERC20s which return bool or not _response: Bytes[32] = raw_call( input_coin, concat( method_id("transferFrom(address,address,uint256)"), convert(msg.sender, bytes32), convert(self, bytes32), convert(dx, bytes32), ), max_outsize=32, ) # dev: failed transfer if len(_response) > 0: assert convert(_response, bool) # dev: failed transfer if i == FEE_INDEX: dx_w_fee = ERC20(input_coin).balanceOf(self) - dx_w_fee x: uint256 = xp[i] + dx_w_fee * rates[i] / PRECISION y: uint256 = self.get_y(i, j, x, xp) dy: uint256 = xp[j] - y - 1 # -1 just in case there were some rounding errors dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR # Convert all to real units dy = (dy - dy_fee) * PRECISION / rates[j] assert dy >= min_dy, "Exchange resulted in fewer coins than expected" dy_admin_fee: uint256 = dy_fee * self.admin_fee / FEE_DENOMINATOR dy_admin_fee = dy_admin_fee * PRECISION / rates[j] # Change balances exactly in same way as we change actual ERC20 coin amounts self.balances[i] = old_balances[i] + dx_w_fee # When rounding errors happen, we undercharge admin fee in favor of LP self.balances[j] = old_balances[j] - dy - dy_admin_fee # "safeTransfer" which works for ERC20s which return bool or not _response = raw_call( self.coins[j], concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(dy, bytes32), ), max_outsize=32, ) # dev: failed transfer if len(_response) > 0: assert convert(_response, bool) # dev: failed transfer log TokenExchange(msg.sender, i, dx, j, dy) @external @nonreentrant('lock') def remove_liquidity(_amount: uint256, min_amounts: uint256[N_COINS]): total_supply: uint256 = self.token.totalSupply() amounts: uint256[N_COINS] = empty(uint256[N_COINS]) fees: uint256[N_COINS] = empty(uint256[N_COINS]) # Fees are unused but we've got them historically in event for i in range(N_COINS): value: uint256 = self.balances[i] * _amount / total_supply assert value >= min_amounts[i], "Withdrawal resulted in fewer coins than expected" self.balances[i] -= value amounts[i] = value # "safeTransfer" which works for ERC20s which return bool or not _response: Bytes[32] = raw_call( self.coins[i], concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(value, bytes32), ), max_outsize=32, ) # dev: failed transfer if len(_response) > 0: assert convert(_response, bool) # dev: failed transfer self.token.burnFrom(msg.sender, _amount) # dev: insufficient funds log RemoveLiquidity(msg.sender, amounts, fees, total_supply - _amount) @external @nonreentrant('lock') def remove_liquidity_imbalance(amounts: uint256[N_COINS], max_burn_amount: uint256): assert not self.is_killed # dev: is killed token_supply: uint256 = self.token.totalSupply() assert token_supply != 0 # dev: zero total supply _fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1)) _admin_fee: uint256 = self.admin_fee amp: uint256 = self._A() old_balances: uint256[N_COINS] = self.balances new_balances: uint256[N_COINS] = old_balances D0: uint256 = self.get_D_mem(old_balances, amp) for i in range(N_COINS): new_balances[i] -= amounts[i] D1: uint256 = self.get_D_mem(new_balances, amp) fees: uint256[N_COINS] = empty(uint256[N_COINS]) for i in range(N_COINS): ideal_balance: uint256 = D1 * old_balances[i] / D0 difference: uint256 = 0 if ideal_balance > new_balances[i]: difference = ideal_balance - new_balances[i] else: difference = new_balances[i] - ideal_balance fees[i] = _fee * difference / FEE_DENOMINATOR self.balances[i] = new_balances[i] - (fees[i] * _admin_fee / FEE_DENOMINATOR) new_balances[i] -= fees[i] D2: uint256 = self.get_D_mem(new_balances, amp) token_amount: uint256 = (D0 - D2) * token_supply / D0 assert token_amount != 0 # dev: zero tokens burned token_amount += 1 # In case of rounding errors - make it unfavorable for the "attacker" assert token_amount <= max_burn_amount, "Slippage screwed you" self.token.burnFrom(msg.sender, token_amount) # dev: insufficient funds for i in range(N_COINS): if amounts[i] != 0: # "safeTransfer" which works for ERC20s which return bool or not _response: Bytes[32] = raw_call( self.coins[i], concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(amounts[i], bytes32), ), max_outsize=32, ) # dev: failed transfer if len(_response) > 0: assert convert(_response, bool) # dev: failed transfer log RemoveLiquidityImbalance(msg.sender, amounts, fees, D1, token_supply - token_amount) @view @internal def get_y_D(A_: uint256, i: int128, xp: uint256[N_COINS], D: uint256) -> uint256: """ Calculate x[i] if one reduces D from being calculated for xp to D Done by solving quadratic equation iteratively. x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) x_1**2 + b*x_1 = c x_1 = (x_1**2 + c) / (2*x_1 + b) """ # x in the input is converted to the same price/precision assert i >= 0 # dev: i below zero assert i < N_COINS # dev: i above N_COINS c: uint256 = D S_: uint256 = 0 Ann: uint256 = A_ * N_COINS _x: uint256 = 0 for _i in range(N_COINS): if _i != i: _x = xp[_i] else: continue S_ += _x c = c * D / (_x * N_COINS) c = c * D / (Ann * N_COINS) b: uint256 = S_ + D / Ann y_prev: uint256 = 0 y: uint256 = D for _i in range(255): y_prev = y y = (y*y + c) / (2 * y + b - D) # Equality with the precision of 1 if y > y_prev: if y - y_prev <= 1: break else: if y_prev - y <= 1: break return y @view @internal def _calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> (uint256, uint256): # First, need to calculate # * Get current D # * Solve Eqn against y_i for D - _token_amount amp: uint256 = self._A() _fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1)) precisions: uint256[N_COINS] = PRECISION_MUL total_supply: uint256 = self.token.totalSupply() xp: uint256[N_COINS] = self._xp() D0: uint256 = self.get_D(xp, amp) D1: uint256 = D0 - _token_amount * D0 / total_supply xp_reduced: uint256[N_COINS] = xp new_y: uint256 = self.get_y_D(amp, i, xp, D1) dy_0: uint256 = (xp[i] - new_y) / precisions[i] # w/o fees for j in range(N_COINS): dx_expected: uint256 = 0 if j == i: dx_expected = xp[j] * D1 / D0 - new_y else: dx_expected = xp[j] - xp[j] * D1 / D0 xp_reduced[j] -= _fee * dx_expected / FEE_DENOMINATOR dy: uint256 = xp_reduced[i] - self.get_y_D(amp, i, xp_reduced, D1) dy = (dy - 1) / precisions[i] # Withdraw less to account for rounding errors return dy, dy_0 - dy @view @external def calc_withdraw_one_coin(_token_amount: uint256, i: int128) -> uint256: return self._calc_withdraw_one_coin(_token_amount, i)[0] @external @nonreentrant('lock') def remove_liquidity_one_coin(_token_amount: uint256, i: int128, min_amount: uint256): """ Remove _amount of liquidity all in a form of coin i """ assert not self.is_killed # dev: is killed dy: uint256 = 0 dy_fee: uint256 = 0 dy, dy_fee = self._calc_withdraw_one_coin(_token_amount, i) assert dy >= min_amount, "Not enough coins removed" self.balances[i] -= (dy + dy_fee * self.admin_fee / FEE_DENOMINATOR) self.token.burnFrom(msg.sender, _token_amount) # dev: insufficient funds # "safeTransfer" which works for ERC20s which return bool or not _response: Bytes[32] = raw_call( self.coins[i], concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(dy, bytes32), ), max_outsize=32, ) # dev: failed transfer if len(_response) > 0: assert convert(_response, bool) # dev: failed transfer log RemoveLiquidityOne(msg.sender, _token_amount, dy) ### Admin functions ### @external def ramp_A(_future_A: uint256, _future_time: uint256): assert msg.sender == self.owner # dev: only owner assert block.timestamp >= self.initial_A_time + MIN_RAMP_TIME assert _future_time >= block.timestamp + MIN_RAMP_TIME # dev: insufficient time _initial_A: uint256 = self._A() assert (_future_A > 0) and (_future_A < MAX_A) assert ((_future_A >= _initial_A) and (_future_A <= _initial_A * MAX_A_CHANGE)) or\ ((_future_A < _initial_A) and (_future_A * MAX_A_CHANGE >= _initial_A)) self.initial_A = _initial_A self.future_A = _future_A self.initial_A_time = block.timestamp self.future_A_time = _future_time log RampA(_initial_A, _future_A, block.timestamp, _future_time) @external def stop_ramp_A(): assert msg.sender == self.owner # dev: only owner current_A: uint256 = self._A() self.initial_A = current_A self.future_A = current_A self.initial_A_time = block.timestamp self.future_A_time = block.timestamp # now (block.timestamp < t1) is always False, so we return saved A log StopRampA(current_A, block.timestamp) @external def commit_new_fee(new_fee: uint256, new_admin_fee: uint256): assert msg.sender == self.owner # dev: only owner assert self.admin_actions_deadline == 0 # dev: active action assert new_fee <= MAX_FEE # dev: fee exceeds maximum assert new_admin_fee <= MAX_ADMIN_FEE # dev: admin fee exceeds maximum _deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY self.admin_actions_deadline = _deadline self.future_fee = new_fee self.future_admin_fee = new_admin_fee log CommitNewFee(_deadline, new_fee, new_admin_fee) @external def apply_new_fee(): assert msg.sender == self.owner # dev: only owner assert block.timestamp >= self.admin_actions_deadline # dev: insufficient time assert self.admin_actions_deadline != 0 # dev: no active action self.admin_actions_deadline = 0 _fee: uint256 = self.future_fee _admin_fee: uint256 = self.future_admin_fee self.fee = _fee self.admin_fee = _admin_fee log NewFee(_fee, _admin_fee) @external def revert_new_parameters(): assert msg.sender == self.owner # dev: only owner self.admin_actions_deadline = 0 @external def commit_transfer_ownership(_owner: address): assert msg.sender == self.owner # dev: only owner assert self.transfer_ownership_deadline == 0 # dev: active transfer _deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY self.transfer_ownership_deadline = _deadline self.future_owner = _owner log CommitNewAdmin(_deadline, _owner) @external def apply_transfer_ownership(): assert msg.sender == self.owner # dev: only owner assert block.timestamp >= self.transfer_ownership_deadline # dev: insufficient time assert self.transfer_ownership_deadline != 0 # dev: no active transfer self.transfer_ownership_deadline = 0 _owner: address = self.future_owner self.owner = _owner log NewAdmin(_owner) @external def revert_transfer_ownership(): assert msg.sender == self.owner # dev: only owner self.transfer_ownership_deadline = 0 @view @external def admin_balances(i: uint256) -> uint256: return ERC20(self.coins[i]).balanceOf(self) - self.balances[i] @external def withdraw_admin_fees(): assert msg.sender == self.owner # dev: only owner for i in range(N_COINS): c: address = self.coins[i] value: uint256 = ERC20(c).balanceOf(self) - self.balances[i] if value > 0: # "safeTransfer" which works for ERC20s which return bool or not _response: Bytes[32] = raw_call( c, concat( method_id("transfer(address,uint256)"), convert(msg.sender, bytes32), convert(value, bytes32), ), max_outsize=32, ) # dev: failed transfer if len(_response) > 0: assert convert(_response, bool) # dev: failed transfer @external def donate_admin_fees(): assert msg.sender == self.owner # dev: only owner for i in range(N_COINS): self.balances[i] = ERC20(self.coins[i]).balanceOf(self) @external def kill_me(): assert msg.sender == self.owner # dev: only owner assert self.kill_deadline > block.timestamp # dev: deadline has passed self.is_killed = True @external def unkill_me(): assert msg.sender == self.owner # dev: only owner self.is_killed = False
File 4 of 5: FiatTokenProxy
pragma solidity ^0.4.24; // File: zos-lib/contracts/upgradeability/Proxy.sol /** * @title Proxy * @dev Implements delegation of calls to other contracts, with proper * forwarding of return values and bubbling of failures. * It defines a fallback function that delegates all calls to the address * returned by the abstract _implementation() internal function. */ contract Proxy { /** * @dev Fallback function. * Implemented entirely in `_fallback`. */ function () payable external { _fallback(); } /** * @return The Address of the implementation. */ function _implementation() internal view returns (address); /** * @dev Delegates execution to an implementation contract. * This is a low level function that doesn't return to its internal call site. * It will return to the external caller whatever the implementation returns. * @param implementation Address to delegate. */ function _delegate(address implementation) internal { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } /** * @dev Function that is run as the first thing in the fallback function. * Can be redefined in derived contracts to add functionality. * Redefinitions must call super._willFallback(). */ function _willFallback() internal { } /** * @dev fallback implementation. * Extracted to enable manual triggering. */ function _fallback() internal { _willFallback(); _delegate(_implementation()); } } // File: openzeppelin-solidity/contracts/AddressUtils.sol /** * Utility library of inline functions on addresses */ library AddressUtils { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param addr address to check * @return whether the target address is a contract */ function isContract(address addr) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solium-disable-next-line security/no-inline-assembly assembly { size := extcodesize(addr) } return size > 0; } } // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol /** * @title UpgradeabilityProxy * @dev This contract implements a proxy that allows to change the * implementation address to which it will delegate. * Such a change is called an implementation upgrade. */ contract UpgradeabilityProxy is Proxy { /** * @dev Emitted when the implementation is upgraded. * @param implementation Address of the new implementation. */ event Upgraded(address implementation); /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is * validated in the constructor. */ bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3; /** * @dev Contract constructor. * @param _implementation Address of the initial implementation. */ constructor(address _implementation) public { assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation")); _setImplementation(_implementation); } /** * @dev Returns the current implementation. * @return Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Upgrades the proxy to a new implementation. * @param newImplementation Address of the new implementation. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Sets the implementation address of the proxy. * @param newImplementation Address of the new implementation. */ function _setImplementation(address newImplementation) private { require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } } // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol /** * @title AdminUpgradeabilityProxy * @dev This contract combines an upgradeability proxy with an authorization * mechanism for administrative tasks. * All external functions in this contract must be guarded by the * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity * feature proposal that would enable this to be done automatically. */ contract AdminUpgradeabilityProxy is UpgradeabilityProxy { /** * @dev Emitted when the administration has been transferred. * @param previousAdmin Address of the previous admin. * @param newAdmin Address of the new admin. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is * validated in the constructor. */ bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b; /** * @dev Modifier to check whether the `msg.sender` is the admin. * If it is, it will run the function. Otherwise, it will delegate the call * to the implementation. */ modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } /** * Contract constructor. * It sets the `msg.sender` as the proxy administrator. * @param _implementation address of the initial implementation. */ constructor(address _implementation) UpgradeabilityProxy(_implementation) public { assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin")); _setAdmin(msg.sender); } /** * @return The address of the proxy admin. */ function admin() external view ifAdmin returns (address) { return _admin(); } /** * @return The address of the implementation. */ function implementation() external view ifAdmin returns (address) { return _implementation(); } /** * @dev Changes the admin of the proxy. * Only the current admin can call this function. * @param newAdmin Address to transfer proxy administration to. */ function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); emit AdminChanged(_admin(), newAdmin); _setAdmin(newAdmin); } /** * @dev Upgrade the backing implementation of the proxy. * Only the admin can call this function. * @param newImplementation Address of the new implementation. */ function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } /** * @dev Upgrade the backing implementation of the proxy and call a function * on the new implementation. * This is useful to initialize the proxied contract. * @param newImplementation Address of the new implementation. * @param data Data to send as msg.data in the low level call. * It should include the signature and the parameters of the function to be * called, as described in * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding. */ function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin { _upgradeTo(newImplementation); require(address(this).call.value(msg.value)(data)); } /** * @return The admin slot. */ function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } /** * @dev Sets the address of the proxy admin. * @param newAdmin Address of the new proxy admin. */ function _setAdmin(address newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, newAdmin) } } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal { require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); super._willFallback(); } } // File: contracts/FiatTokenProxy.sol /** * Copyright CENTRE SECZ 2018 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished to * do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ pragma solidity ^0.4.24; /** * @title FiatTokenProxy * @dev This contract proxies FiatToken calls and enables FiatToken upgrades */ contract FiatTokenProxy is AdminUpgradeabilityProxy { constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) { } }
File 5 of 5: FiatTokenV2_1
// File: @openzeppelin/contracts/math/SafeMath.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @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 subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ 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 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) { 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; } } // File: @openzeppelin/contracts/token/ERC20/IERC20.sol pragma solidity ^0.6.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval( address indexed owner, address indexed spender, uint256 value ); } // File: contracts/v1/AbstractFiatTokenV1.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; abstract contract AbstractFiatTokenV1 is IERC20 { function _approve( address owner, address spender, uint256 value ) internal virtual; function _transfer( address from, address to, uint256 value ) internal virtual; } // File: contracts/v1/Ownable.sol /** * Copyright (c) 2018 zOS Global Limited. * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @notice The Ownable contract has an owner address, and provides basic * authorization control functions * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol * Modifications: * 1. Consolidate OwnableStorage into this contract (7/13/18) * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20) * 3. Make public functions external (5/27/20) */ contract Ownable { // Owner of the contract address private _owner; /** * @dev Event to show ownership has been transferred * @param previousOwner representing the address of the previous owner * @param newOwner representing the address of the new owner */ event OwnershipTransferred(address previousOwner, address newOwner); /** * @dev The constructor sets the original owner of the contract to the sender account. */ constructor() public { setOwner(msg.sender); } /** * @dev Tells the address of the owner * @return the address of the owner */ function owner() external view returns (address) { return _owner; } /** * @dev Sets a new owner address */ function setOwner(address newOwner) internal { _owner = newOwner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == _owner, "Ownable: caller is not the owner"); _; } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) external onlyOwner { require( newOwner != address(0), "Ownable: new owner is the zero address" ); emit OwnershipTransferred(_owner, newOwner); setOwner(newOwner); } } // File: contracts/v1/Pausable.sol /** * Copyright (c) 2016 Smart Contract Solutions, Inc. * Copyright (c) 2018-2020 CENTRE SECZ0 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @notice Base contract which allows children to implement an emergency stop * mechanism * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol * Modifications: * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018) * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018) * 3. Removed whenPaused (6/14/2018) * 4. Switches ownable library to use ZeppelinOS (7/12/18) * 5. Remove constructor (7/13/18) * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20) * 7. Make public functions external (5/27/20) */ contract Pausable is Ownable { event Pause(); event Unpause(); event PauserChanged(address indexed newAddress); address public pauser; bool public paused = false; /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!paused, "Pausable: paused"); _; } /** * @dev throws if called by any account other than the pauser */ modifier onlyPauser() { require(msg.sender == pauser, "Pausable: caller is not the pauser"); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() external onlyPauser { paused = true; emit Pause(); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() external onlyPauser { paused = false; emit Unpause(); } /** * @dev update the pauser role */ function updatePauser(address _newPauser) external onlyOwner { require( _newPauser != address(0), "Pausable: new pauser is the zero address" ); pauser = _newPauser; emit PauserChanged(pauser); } } // File: contracts/v1/Blacklistable.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title Blacklistable Token * @dev Allows accounts to be blacklisted by a "blacklister" role */ contract Blacklistable is Ownable { address public blacklister; mapping(address => bool) internal blacklisted; event Blacklisted(address indexed _account); event UnBlacklisted(address indexed _account); event BlacklisterChanged(address indexed newBlacklister); /** * @dev Throws if called by any account other than the blacklister */ modifier onlyBlacklister() { require( msg.sender == blacklister, "Blacklistable: caller is not the blacklister" ); _; } /** * @dev Throws if argument account is blacklisted * @param _account The address to check */ modifier notBlacklisted(address _account) { require( !blacklisted[_account], "Blacklistable: account is blacklisted" ); _; } /** * @dev Checks if account is blacklisted * @param _account The address to check */ function isBlacklisted(address _account) external view returns (bool) { return blacklisted[_account]; } /** * @dev Adds account to blacklist * @param _account The address to blacklist */ function blacklist(address _account) external onlyBlacklister { blacklisted[_account] = true; emit Blacklisted(_account); } /** * @dev Removes account from blacklist * @param _account The address to remove from the blacklist */ function unBlacklist(address _account) external onlyBlacklister { blacklisted[_account] = false; emit UnBlacklisted(_account); } function updateBlacklister(address _newBlacklister) external onlyOwner { require( _newBlacklister != address(0), "Blacklistable: new blacklister is the zero address" ); blacklister = _newBlacklister; emit BlacklisterChanged(blacklister); } } // File: contracts/v1/FiatTokenV1.sol /** * * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title FiatToken * @dev ERC20 Token backed by fiat reserves */ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable { using SafeMath for uint256; string public name; string public symbol; uint8 public decimals; string public currency; address public masterMinter; bool internal initialized; mapping(address => uint256) internal balances; mapping(address => mapping(address => uint256)) internal allowed; uint256 internal totalSupply_ = 0; mapping(address => bool) internal minters; mapping(address => uint256) internal minterAllowed; event Mint(address indexed minter, address indexed to, uint256 amount); event Burn(address indexed burner, uint256 amount); event MinterConfigured(address indexed minter, uint256 minterAllowedAmount); event MinterRemoved(address indexed oldMinter); event MasterMinterChanged(address indexed newMasterMinter); function initialize( string memory tokenName, string memory tokenSymbol, string memory tokenCurrency, uint8 tokenDecimals, address newMasterMinter, address newPauser, address newBlacklister, address newOwner ) public { require(!initialized, "FiatToken: contract is already initialized"); require( newMasterMinter != address(0), "FiatToken: new masterMinter is the zero address" ); require( newPauser != address(0), "FiatToken: new pauser is the zero address" ); require( newBlacklister != address(0), "FiatToken: new blacklister is the zero address" ); require( newOwner != address(0), "FiatToken: new owner is the zero address" ); name = tokenName; symbol = tokenSymbol; currency = tokenCurrency; decimals = tokenDecimals; masterMinter = newMasterMinter; pauser = newPauser; blacklister = newBlacklister; setOwner(newOwner); initialized = true; } /** * @dev Throws if called by any account other than a minter */ modifier onlyMinters() { require(minters[msg.sender], "FiatToken: caller is not a minter"); _; } /** * @dev Function to mint tokens * @param _to The address that will receive the minted tokens. * @param _amount The amount of tokens to mint. Must be less than or equal * to the minterAllowance of the caller. * @return A boolean that indicates if the operation was successful. */ function mint(address _to, uint256 _amount) external whenNotPaused onlyMinters notBlacklisted(msg.sender) notBlacklisted(_to) returns (bool) { require(_to != address(0), "FiatToken: mint to the zero address"); require(_amount > 0, "FiatToken: mint amount not greater than 0"); uint256 mintingAllowedAmount = minterAllowed[msg.sender]; require( _amount <= mintingAllowedAmount, "FiatToken: mint amount exceeds minterAllowance" ); totalSupply_ = totalSupply_.add(_amount); balances[_to] = balances[_to].add(_amount); minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount); emit Mint(msg.sender, _to, _amount); emit Transfer(address(0), _to, _amount); return true; } /** * @dev Throws if called by any account other than the masterMinter */ modifier onlyMasterMinter() { require( msg.sender == masterMinter, "FiatToken: caller is not the masterMinter" ); _; } /** * @dev Get minter allowance for an account * @param minter The address of the minter */ function minterAllowance(address minter) external view returns (uint256) { return minterAllowed[minter]; } /** * @dev Checks if account is a minter * @param account The address to check */ function isMinter(address account) external view returns (bool) { return minters[account]; } /** * @notice Amount of remaining tokens spender is allowed to transfer on * behalf of the token owner * @param owner Token owner's address * @param spender Spender's address * @return Allowance amount */ function allowance(address owner, address spender) external override view returns (uint256) { return allowed[owner][spender]; } /** * @dev Get totalSupply of token */ function totalSupply() external override view returns (uint256) { return totalSupply_; } /** * @dev Get token balance of an account * @param account address The account */ function balanceOf(address account) external override view returns (uint256) { return balances[account]; } /** * @notice Set spender's allowance over the caller's tokens to be a given * value. * @param spender Spender's address * @param value Allowance amount * @return True if successful */ function approve(address spender, uint256 value) external override whenNotPaused notBlacklisted(msg.sender) notBlacklisted(spender) returns (bool) { _approve(msg.sender, spender, value); return true; } /** * @dev Internal function to set allowance * @param owner Token owner's address * @param spender Spender's address * @param value Allowance amount */ function _approve( address owner, address spender, uint256 value ) internal override { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); allowed[owner][spender] = value; emit Approval(owner, spender, value); } /** * @notice Transfer tokens by spending allowance * @param from Payer's address * @param to Payee's address * @param value Transfer amount * @return True if successful */ function transferFrom( address from, address to, uint256 value ) external override whenNotPaused notBlacklisted(msg.sender) notBlacklisted(from) notBlacklisted(to) returns (bool) { require( value <= allowed[from][msg.sender], "ERC20: transfer amount exceeds allowance" ); _transfer(from, to, value); allowed[from][msg.sender] = allowed[from][msg.sender].sub(value); return true; } /** * @notice Transfer tokens from the caller * @param to Payee's address * @param value Transfer amount * @return True if successful */ function transfer(address to, uint256 value) external override whenNotPaused notBlacklisted(msg.sender) notBlacklisted(to) returns (bool) { _transfer(msg.sender, to, value); return true; } /** * @notice Internal function to process transfers * @param from Payer's address * @param to Payee's address * @param value Transfer amount */ function _transfer( address from, address to, uint256 value ) internal override { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); require( value <= balances[from], "ERC20: transfer amount exceeds balance" ); balances[from] = balances[from].sub(value); balances[to] = balances[to].add(value); emit Transfer(from, to, value); } /** * @dev Function to add/update a new minter * @param minter The address of the minter * @param minterAllowedAmount The minting amount allowed for the minter * @return True if the operation was successful. */ function configureMinter(address minter, uint256 minterAllowedAmount) external whenNotPaused onlyMasterMinter returns (bool) { minters[minter] = true; minterAllowed[minter] = minterAllowedAmount; emit MinterConfigured(minter, minterAllowedAmount); return true; } /** * @dev Function to remove a minter * @param minter The address of the minter to remove * @return True if the operation was successful. */ function removeMinter(address minter) external onlyMasterMinter returns (bool) { minters[minter] = false; minterAllowed[minter] = 0; emit MinterRemoved(minter); return true; } /** * @dev allows a minter to burn some of its own tokens * Validates that caller is a minter and that sender is not blacklisted * amount is less than or equal to the minter's account balance * @param _amount uint256 the amount of tokens to be burned */ function burn(uint256 _amount) external whenNotPaused onlyMinters notBlacklisted(msg.sender) { uint256 balance = balances[msg.sender]; require(_amount > 0, "FiatToken: burn amount not greater than 0"); require(balance >= _amount, "FiatToken: burn amount exceeds balance"); totalSupply_ = totalSupply_.sub(_amount); balances[msg.sender] = balance.sub(_amount); emit Burn(msg.sender, _amount); emit Transfer(msg.sender, address(0), _amount); } function updateMasterMinter(address _newMasterMinter) external onlyOwner { require( _newMasterMinter != address(0), "FiatToken: new masterMinter is the zero address" ); masterMinter = _newMasterMinter; emit MasterMinterChanged(masterMinter); } } // File: @openzeppelin/contracts/utils/Address.sol pragma solidity ^0.6.2; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require( address(this).balance >= amount, "Address: insufficient balance" ); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require( success, "Address: unable to send value, recipient may have reverted" ); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, "Address: low-level call with value failed" ); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require( address(this).balance >= value, "Address: insufficient balance for call" ); return _functionCallWithValue(target, data, value, errorMessage); } function _functionCallWithValue( address target, bytes memory data, uint256 weiValue, string memory errorMessage ) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol pragma solidity ^0.6.0; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).add( value ); _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).sub( value, "SafeERC20: decreased allowance below zero" ); _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall( data, "SafeERC20: low-level call failed" ); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require( abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed" ); } } } // File: contracts/v1.1/Rescuable.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; contract Rescuable is Ownable { using SafeERC20 for IERC20; address private _rescuer; event RescuerChanged(address indexed newRescuer); /** * @notice Returns current rescuer * @return Rescuer's address */ function rescuer() external view returns (address) { return _rescuer; } /** * @notice Revert if called by any account other than the rescuer. */ modifier onlyRescuer() { require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer"); _; } /** * @notice Rescue ERC20 tokens locked up in this contract. * @param tokenContract ERC20 token contract address * @param to Recipient address * @param amount Amount to withdraw */ function rescueERC20( IERC20 tokenContract, address to, uint256 amount ) external onlyRescuer { tokenContract.safeTransfer(to, amount); } /** * @notice Assign the rescuer role to a given address. * @param newRescuer New rescuer's address */ function updateRescuer(address newRescuer) external onlyOwner { require( newRescuer != address(0), "Rescuable: new rescuer is the zero address" ); _rescuer = newRescuer; emit RescuerChanged(newRescuer); } } // File: contracts/v1.1/FiatTokenV1_1.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title FiatTokenV1_1 * @dev ERC20 Token backed by fiat reserves */ contract FiatTokenV1_1 is FiatTokenV1, Rescuable { } // File: contracts/v2/AbstractFiatTokenV2.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 { function _increaseAllowance( address owner, address spender, uint256 increment ) internal virtual; function _decreaseAllowance( address owner, address spender, uint256 decrement ) internal virtual; } // File: contracts/util/ECRecover.sol /** * Copyright (c) 2016-2019 zOS Global Limited * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title ECRecover * @notice A library that provides a safe ECDSA recovery function */ library ECRecover { /** * @notice Recover signer's address from a signed message * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol * Modifications: Accept v, r, and s as separate arguments * @param digest Keccak-256 hash digest of the signed message * @param v v of the signature * @param r r of the signature * @param s s of the signature * @return Signer address */ function recover( bytes32 digest, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if ( uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 ) { revert("ECRecover: invalid signature 's' value"); } if (v != 27 && v != 28) { revert("ECRecover: invalid signature 'v' value"); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(digest, v, r, s); require(signer != address(0), "ECRecover: invalid signature"); return signer; } } // File: contracts/util/EIP712.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title EIP712 * @notice A library that provides EIP712 helper functions */ library EIP712 { /** * @notice Make EIP712 domain separator * @param name Contract name * @param version Contract version * @return Domain separator */ function makeDomainSeparator(string memory name, string memory version) internal view returns (bytes32) { uint256 chainId; assembly { chainId := chainid() } return keccak256( abi.encode( // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, keccak256(bytes(name)), keccak256(bytes(version)), chainId, address(this) ) ); } /** * @notice Recover signer's address from a EIP712 signature * @param domainSeparator Domain separator * @param v v of the signature * @param r r of the signature * @param s s of the signature * @param typeHashAndData Type hash concatenated with data * @return Signer's address */ function recover( bytes32 domainSeparator, uint8 v, bytes32 r, bytes32 s, bytes memory typeHashAndData ) internal pure returns (address) { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", domainSeparator, keccak256(typeHashAndData) ) ); return ECRecover.recover(digest, v, r, s); } } // File: contracts/v2/EIP712Domain.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title EIP712 Domain */ contract EIP712Domain { /** * @dev EIP712 Domain Separator */ bytes32 public DOMAIN_SEPARATOR; } // File: contracts/v2/EIP3009.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title EIP-3009 * @notice Provide internal implementation for gas-abstracted transfers * @dev Contracts that inherit from this must wrap these with publicly * accessible functions, optionally adding modifiers where necessary */ abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain { // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)") bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429; /** * @dev authorizer address => nonce => bool (true if nonce is used) */ mapping(address => mapping(bytes32 => bool)) private _authorizationStates; event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); event AuthorizationCanceled( address indexed authorizer, bytes32 indexed nonce ); /** * @notice Returns the state of an authorization * @dev Nonces are randomly generated 32-byte data unique to the * authorizer's address * @param authorizer Authorizer's address * @param nonce Nonce of the authorization * @return True if the nonce is used */ function authorizationState(address authorizer, bytes32 nonce) external view returns (bool) { return _authorizationStates[authorizer][nonce]; } /** * @notice Execute a transfer with a signed authorization * @param from Payer's address (Authorizer) * @param to Payee's address * @param value Amount to be transferred * @param validAfter The time after which this is valid (unix time) * @param validBefore The time before which this is valid (unix time) * @param nonce Unique nonce * @param v v of the signature * @param r r of the signature * @param s s of the signature */ function _transferWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s ) internal { _requireValidAuthorization(from, nonce, validAfter, validBefore); bytes memory data = abi.encode( TRANSFER_WITH_AUTHORIZATION_TYPEHASH, from, to, value, validAfter, validBefore, nonce ); require( EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, "FiatTokenV2: invalid signature" ); _markAuthorizationAsUsed(from, nonce); _transfer(from, to, value); } /** * @notice Receive a transfer with a signed authorization from the payer * @dev This has an additional check to ensure that the payee's address * matches the caller of this function to prevent front-running attacks. * @param from Payer's address (Authorizer) * @param to Payee's address * @param value Amount to be transferred * @param validAfter The time after which this is valid (unix time) * @param validBefore The time before which this is valid (unix time) * @param nonce Unique nonce * @param v v of the signature * @param r r of the signature * @param s s of the signature */ function _receiveWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s ) internal { require(to == msg.sender, "FiatTokenV2: caller must be the payee"); _requireValidAuthorization(from, nonce, validAfter, validBefore); bytes memory data = abi.encode( RECEIVE_WITH_AUTHORIZATION_TYPEHASH, from, to, value, validAfter, validBefore, nonce ); require( EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, "FiatTokenV2: invalid signature" ); _markAuthorizationAsUsed(from, nonce); _transfer(from, to, value); } /** * @notice Attempt to cancel an authorization * @param authorizer Authorizer's address * @param nonce Nonce of the authorization * @param v v of the signature * @param r r of the signature * @param s s of the signature */ function _cancelAuthorization( address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s ) internal { _requireUnusedAuthorization(authorizer, nonce); bytes memory data = abi.encode( CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce ); require( EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == authorizer, "FiatTokenV2: invalid signature" ); _authorizationStates[authorizer][nonce] = true; emit AuthorizationCanceled(authorizer, nonce); } /** * @notice Check that an authorization is unused * @param authorizer Authorizer's address * @param nonce Nonce of the authorization */ function _requireUnusedAuthorization(address authorizer, bytes32 nonce) private view { require( !_authorizationStates[authorizer][nonce], "FiatTokenV2: authorization is used or canceled" ); } /** * @notice Check that authorization is valid * @param authorizer Authorizer's address * @param nonce Nonce of the authorization * @param validAfter The time after which this is valid (unix time) * @param validBefore The time before which this is valid (unix time) */ function _requireValidAuthorization( address authorizer, bytes32 nonce, uint256 validAfter, uint256 validBefore ) private view { require( now > validAfter, "FiatTokenV2: authorization is not yet valid" ); require(now < validBefore, "FiatTokenV2: authorization is expired"); _requireUnusedAuthorization(authorizer, nonce); } /** * @notice Mark an authorization as used * @param authorizer Authorizer's address * @param nonce Nonce of the authorization */ function _markAuthorizationAsUsed(address authorizer, bytes32 nonce) private { _authorizationStates[authorizer][nonce] = true; emit AuthorizationUsed(authorizer, nonce); } } // File: contracts/v2/EIP2612.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title EIP-2612 * @notice Provide internal implementation for gas-abstracted approvals */ abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain { // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint256) private _permitNonces; /** * @notice Nonces for permit * @param owner Token owner's address (Authorizer) * @return Next nonce */ function nonces(address owner) external view returns (uint256) { return _permitNonces[owner]; } /** * @notice Verify a signed approval permit and execute if valid * @param owner Token owner's address (Authorizer) * @param spender Spender's address * @param value Amount of allowance * @param deadline The time at which this expires (unix time) * @param v v of the signature * @param r r of the signature * @param s s of the signature */ function _permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { require(deadline >= now, "FiatTokenV2: permit is expired"); bytes memory data = abi.encode( PERMIT_TYPEHASH, owner, spender, value, _permitNonces[owner]++, deadline ); require( EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner, "EIP2612: invalid signature" ); _approve(owner, spender, value); } } // File: contracts/v2/FiatTokenV2.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; /** * @title FiatToken V2 * @notice ERC20 Token backed by fiat reserves, version 2 */ contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 { uint8 internal _initializedVersion; /** * @notice Initialize v2 * @param newName New token name */ function initializeV2(string calldata newName) external { // solhint-disable-next-line reason-string require(initialized && _initializedVersion == 0); name = newName; DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(newName, "2"); _initializedVersion = 1; } /** * @notice Increase the allowance by a given increment * @param spender Spender's address * @param increment Amount of increase in allowance * @return True if successful */ function increaseAllowance(address spender, uint256 increment) external whenNotPaused notBlacklisted(msg.sender) notBlacklisted(spender) returns (bool) { _increaseAllowance(msg.sender, spender, increment); return true; } /** * @notice Decrease the allowance by a given decrement * @param spender Spender's address * @param decrement Amount of decrease in allowance * @return True if successful */ function decreaseAllowance(address spender, uint256 decrement) external whenNotPaused notBlacklisted(msg.sender) notBlacklisted(spender) returns (bool) { _decreaseAllowance(msg.sender, spender, decrement); return true; } /** * @notice Execute a transfer with a signed authorization * @param from Payer's address (Authorizer) * @param to Payee's address * @param value Amount to be transferred * @param validAfter The time after which this is valid (unix time) * @param validBefore The time before which this is valid (unix time) * @param nonce Unique nonce * @param v v of the signature * @param r r of the signature * @param s s of the signature */ function transferWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { _transferWithAuthorization( from, to, value, validAfter, validBefore, nonce, v, r, s ); } /** * @notice Receive a transfer with a signed authorization from the payer * @dev This has an additional check to ensure that the payee's address * matches the caller of this function to prevent front-running attacks. * @param from Payer's address (Authorizer) * @param to Payee's address * @param value Amount to be transferred * @param validAfter The time after which this is valid (unix time) * @param validBefore The time before which this is valid (unix time) * @param nonce Unique nonce * @param v v of the signature * @param r r of the signature * @param s s of the signature */ function receiveWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) { _receiveWithAuthorization( from, to, value, validAfter, validBefore, nonce, v, r, s ); } /** * @notice Attempt to cancel an authorization * @dev Works only if the authorization is not yet used. * @param authorizer Authorizer's address * @param nonce Nonce of the authorization * @param v v of the signature * @param r r of the signature * @param s s of the signature */ function cancelAuthorization( address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s ) external whenNotPaused { _cancelAuthorization(authorizer, nonce, v, r, s); } /** * @notice Update allowance with a signed permit * @param owner Token owner's address (Authorizer) * @param spender Spender's address * @param value Amount of allowance * @param deadline Expiration time, seconds since the epoch * @param v v of the signature * @param r r of the signature * @param s s of the signature */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external whenNotPaused notBlacklisted(owner) notBlacklisted(spender) { _permit(owner, spender, value, deadline, v, r, s); } /** * @notice Internal function to increase the allowance by a given increment * @param owner Token owner's address * @param spender Spender's address * @param increment Amount of increase */ function _increaseAllowance( address owner, address spender, uint256 increment ) internal override { _approve(owner, spender, allowed[owner][spender].add(increment)); } /** * @notice Internal function to decrease the allowance by a given decrement * @param owner Token owner's address * @param spender Spender's address * @param decrement Amount of decrease */ function _decreaseAllowance( address owner, address spender, uint256 decrement ) internal override { _approve( owner, spender, allowed[owner][spender].sub( decrement, "ERC20: decreased allowance below zero" ) ); } } // File: contracts/v2/FiatTokenV2_1.sol /** * Copyright (c) 2018-2020 CENTRE SECZ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity 0.6.12; // solhint-disable func-name-mixedcase /** * @title FiatToken V2.1 * @notice ERC20 Token backed by fiat reserves, version 2.1 */ contract FiatTokenV2_1 is FiatTokenV2 { /** * @notice Initialize v2.1 * @param lostAndFound The address to which the locked funds are sent */ function initializeV2_1(address lostAndFound) external { // solhint-disable-next-line reason-string require(_initializedVersion == 1); uint256 lockedAmount = balances[address(this)]; if (lockedAmount > 0) { _transfer(address(this), lostAndFound, lockedAmount); } blacklisted[address(this)] = true; _initializedVersion = 2; } /** * @notice Version string for the EIP712 domain separator * @return Version string */ function version() external view returns (string memory) { return "2"; } }