class TestToken: def setup_class(self): self.token = Token( "COW", Address('0xbeef00000000000000000000000000000000BEEF'), 4) def test_convert(self): # two chain_amount = Wad(20000) assert self.token.normalize_amount(chain_amount) == Wad.from_number(2) # three normalized_amount = Wad.from_number(3) assert self.token.unnormalize_amount(normalized_amount) == Wad(30000) def test_min_amount(self): assert self.token.min_amount == Wad.from_number(0.0001) assert float(self.token.min_amount) == 0.0001 assert self.token.unnormalize_amount(self.token.min_amount) == Wad(1) assert Wad.from_number(0.0004) > self.token.min_amount assert Wad.from_number(0.00005) < self.token.min_amount assert self.token.unnormalize_amount( Wad.from_number(0.0006)) > self.token.unnormalize_amount( self.token.min_amount) assert self.token.unnormalize_amount( Wad.from_number(0.00007)) < self.token.unnormalize_amount( self.token.min_amount) assert self.token.unnormalize_amount( Wad.from_number(0.00008)) == Wad(0)
def set_and_approve_pair_token(self, pair_address: Address): self.pair_address = pair_address self._pair_contract = self._get_contract(self.web3, self.pair_abi['abi'], self.pair_address) self.pair_token = Token('Liquidity', self.pair_address, 18) self.is_new_pool = False
def get_exchange_balance(self, token: Token, pair_address: Address) -> Wad: assert (isinstance(token, Token)) assert (isinstance(pair_address, Address)) return token.normalize_amount( ERC20Token(web3=self.web3, address=token.address).balance_of(pair_address))
def setup_method(self): # Use Ganache docker container self.web3 = Web3(HTTPProvider("http://0.0.0.0:8555")) self.web3.eth.defaultAccount = Web3.toChecksumAddress( "0x9596C16D7bF9323265C2F2E22f43e6c80eB3d943") register_private_key( self.web3, "0x91cf2cc3671a365fcbf38010ff97ee31a5b7e674842663c56769e41600696ead" ) self.our_address = Address(self.web3.eth.defaultAccount) self.weth_address = self._deploy(self.web3, self.weth_abi, self.weth_bin, []) self.factory_address = self._deploy(self.web3, self.factory_abi, self.factory_bin, [self.our_address.address]) self.router_address = self._deploy( self.web3, self.router_abi, self.router_bin, [self.factory_address.address, self.weth_address.address]) self._weth_contract = self._get_contract(self.web3, self.weth_abi, self.weth_address) self.ds_systemcoin = DSToken.deploy(self.web3, 'SystemCoin', 'sys') self.ds_usdc = DSToken.deploy(self.web3, 'USDC', 'USDC') self.token_systemcoin = Token("SystemCoin", self.ds_systemcoin.address, 18) self.token_usdc = Token("USDC", self.ds_usdc.address, 6) self.token_weth = Token("WETH", self.weth_address, 18) self.systemcoin_usdc_uniswap = UniswapV2( self.web3, self.token_systemcoin, self.token_usdc, self.our_address, self.router_address, self.factory_address) self.systemcoin_eth_uniswap = UniswapV2( self.web3, self.token_systemcoin, self.token_weth, self.our_address, self.router_address, self.factory_address) ## Useful for debugging failing transactions logger = logging.getLogger('eth') logger.setLevel(8)
# reduce logspew logging.getLogger('urllib3').setLevel(logging.INFO) logging.getLogger("web3").setLevel(logging.INFO) logging.getLogger("asyncio").setLevel(logging.INFO) logging.getLogger("requests").setLevel(logging.INFO) # Usage: # python3 tests/manual_test_create_unsafe_vault [ADDRESS] [KEY] [COLLATERAL_TYPE] geb = GfDeployment.from_node(web3, 'rai') our_address = Address(web3.eth.defaultAccount) collateral = geb.collaterals[str( sys.argv[3])] if len(sys.argv) > 3 else geb.collaterals['ETH-A'] collateral_type = geb.safe_engine.collateral_type( collateral.collateral_type.name) token = Token(collateral.collateral.symbol(), collateral.collateral.address, collateral.adapter.decimals()) safe = geb.safe_engine.safe(collateral.collateral_type, our_address) # geb.approve_system_coin(our_address) # Transact.gas_estimate_for_bad_txs = 20000 osm_price = collateral.osm.peek() redemption_price = geb.oracle_relayer.redemption_price() action = sys.argv[4] if len(sys.argv) > 4 else "create" def r(value, decimals=1): return round(float(value), decimals) logging.info( f"{collateral_type.name:<6}: debt_floor={r(collateral_type.debt_floor)} osm_price={osm_price} safety_ratio={r(geb.oracle_relayer.safety_c_ratio(collateral_type))}" )
class TestUniswapV2(Contract): """ In order to run automated tests locally, all dependent contracts and deployable bytecode need to be available for deploying contract to local network. Deployable bytecode differs from the runtime bytecode you would see on Etherscan. """ pair_abi = Contract._load_abi(__name__, '../pyexchange/abi/IUniswapV2Pair.abi') Irouter_abi = Contract._load_abi( __name__, '../pyexchange/abi/IUniswapV2Router02.abi')['abi'] router_abi = Contract._load_abi(__name__, '../pyexchange/abi/UniswapV2Router02.abi') router_bin = Contract._load_bin(__name__, '../pyexchange/abi/UniswapV2Router02.bin') factory_abi = Contract._load_abi(__name__, '../pyexchange/abi/UniswapV2Factory.abi') factory_bin = Contract._load_bin(__name__, '../pyexchange/abi/UniswapV2Factory.bin') weth_abi = Contract._load_abi(__name__, '../pyexchange/abi/WETH.abi') weth_bin = Contract._load_bin(__name__, '../pyexchange/abi/WETH.bin') def setup_method(self): # Use Ganache docker container self.web3 = Web3(HTTPProvider("http://0.0.0.0:8555")) self.web3.eth.defaultAccount = Web3.toChecksumAddress( "0x9596C16D7bF9323265C2F2E22f43e6c80eB3d943") register_private_key( self.web3, "0x91cf2cc3671a365fcbf38010ff97ee31a5b7e674842663c56769e41600696ead" ) self.our_address = Address(self.web3.eth.defaultAccount) self.weth_address = self._deploy(self.web3, self.weth_abi, self.weth_bin, []) self.factory_address = self._deploy(self.web3, self.factory_abi, self.factory_bin, [self.our_address.address]) self.router_address = self._deploy( self.web3, self.router_abi, self.router_bin, [self.factory_address.address, self.weth_address.address]) self._weth_contract = self._get_contract(self.web3, self.weth_abi, self.weth_address) self.ds_systemcoin = DSToken.deploy(self.web3, 'SystemCoin', 'sys') self.ds_usdc = DSToken.deploy(self.web3, 'USDC', 'USDC') self.token_systemcoin = Token("SystemCoin", self.ds_systemcoin.address, 18) self.token_usdc = Token("USDC", self.ds_usdc.address, 6) self.token_weth = Token("WETH", self.weth_address, 18) self.systemcoin_usdc_uniswap = UniswapV2( self.web3, self.token_systemcoin, self.token_usdc, self.our_address, self.router_address, self.factory_address) self.systemcoin_eth_uniswap = UniswapV2( self.web3, self.token_systemcoin, self.token_weth, self.our_address, self.router_address, self.factory_address) ## Useful for debugging failing transactions logger = logging.getLogger('eth') logger.setLevel(8) # Transact.gas_estimate_for_bad_txs = 210000 def add_liquidity_tokens(self) -> Receipt: self.ds_systemcoin.mint(Wad( 17 * 10**18)).transact(from_address=self.our_address) self.ds_usdc.mint( self.token_usdc.unnormalize_amount( Wad.from_number(9))).transact(from_address=self.our_address) self.systemcoin_usdc_uniswap.approve(self.token_systemcoin) self.systemcoin_usdc_uniswap.approve(self.token_usdc) add_liquidity_tokens_args = { "amount_a_desired": Wad.from_number(1.9), "amount_b_desired": self.token_usdc.unnormalize_amount(Wad.from_number(2.0)), "amount_a_min": Wad.from_number(1.8), "amount_b_min": self.token_usdc.unnormalize_amount(Wad.from_number(1.9)) } return self.systemcoin_usdc_uniswap.add_liquidity( add_liquidity_tokens_args, self.token_systemcoin, self.token_usdc).transact(from_address=self.our_address) def add_liquidity_eth(self) -> Receipt: self.ds_systemcoin.mint(Wad( 300 * 10**18)).transact(from_address=self.our_address) self.systemcoin_eth_uniswap.approve(self.token_systemcoin) self.systemcoin_eth_uniswap.approve(self.token_weth) add_liquidity_eth_args = { "amount_b_desired": Wad.from_number(28), "amount_a_desired": Wad.from_number(.1), "amount_b_min": Wad.from_number(25), "amount_a_min": Wad.from_number(0.01) } return self.systemcoin_eth_uniswap.add_liquidity_eth( add_liquidity_eth_args, self.token_systemcoin, 0).transact(from_address=self.our_address) def test_approval(self): # given assert self.ds_systemcoin.allowance_of(self.our_address, self.router_address) == Wad(0) # when self.systemcoin_usdc_uniswap.approve(self.token_systemcoin) # then assert self.ds_systemcoin.allowance_of(self.our_address, self.router_address) > Wad(0) def test_getting_token_balances(self): # given self.ds_systemcoin.mint(Wad(17 * 10**18)).transact() self.ds_usdc.mint( self.token_usdc.unnormalize_amount(Wad.from_number(9))).transact() # when balance_systemcoin = self.systemcoin_usdc_uniswap.get_account_token_balance( self.token_systemcoin) balance_usdc = self.systemcoin_usdc_uniswap.get_account_token_balance( self.token_usdc) # then assert balance_systemcoin == Wad.from_number(17) assert balance_usdc == Wad.from_number(9) def test_add_liquidity_tokens(self): # when add_liquidity = self.add_liquidity_tokens() # then assert add_liquidity.successful == True # when self.systemcoin_usdc_uniswap.set_and_approve_pair_token( self.systemcoin_usdc_uniswap.get_pair_address( self.token_systemcoin.address, self.token_usdc.address)) # then assert self.systemcoin_usdc_uniswap.get_current_liquidity( ) > Wad.from_number(0) def test_add_liquidity_eth(self): # when add_liquidity_eth = self.add_liquidity_eth() # then assert add_liquidity_eth.successful == True # when self.systemcoin_eth_uniswap.set_and_approve_pair_token( self.systemcoin_usdc_uniswap.get_pair_address( self.token_systemcoin.address, self.token_weth.address)) # then assert self.systemcoin_eth_uniswap.get_current_liquidity( ) > Wad.from_number(0) def test_remove_liquidity_tokens(self): # given add_liquidity = self.add_liquidity_tokens() self.systemcoin_usdc_uniswap.set_and_approve_pair_token( self.systemcoin_usdc_uniswap.get_pair_address( self.token_systemcoin.address, self.token_usdc.address)) current_liquidity = self.systemcoin_usdc_uniswap.get_current_liquidity( ) total_liquidity = self.systemcoin_usdc_uniswap.get_total_liquidity() systemcoin_exchange_balance = self.systemcoin_usdc_uniswap.get_exchange_balance( self.token_systemcoin, self.systemcoin_usdc_uniswap.pair_address) usdc_exchange_balance = self.token_usdc.unnormalize_amount( self.systemcoin_usdc_uniswap.get_exchange_balance( self.token_usdc, self.systemcoin_usdc_uniswap.pair_address)) # then assert current_liquidity > Wad.from_number(0) assert total_liquidity > Wad.from_number(0) assert total_liquidity > current_liquidity # given amount_a_min = current_liquidity * systemcoin_exchange_balance / total_liquidity amount_b_min = current_liquidity * usdc_exchange_balance / total_liquidity remove_liquidity_tokens_args = { "liquidity": current_liquidity, "amountAMin": amount_a_min, "amountBMin": amount_b_min } # when remove_liquidity = self.systemcoin_usdc_uniswap.remove_liquidity( remove_liquidity_tokens_args, self.token_systemcoin, self.token_usdc).transact(from_address=self.our_address) # then assert remove_liquidity.successful == True assert self.systemcoin_usdc_uniswap.get_current_liquidity( ) == Wad.from_number(0) def test_remove_liquidity_eth(self): # given add_liquidity_eth = self.add_liquidity_eth() self.systemcoin_eth_uniswap.set_and_approve_pair_token( self.systemcoin_eth_uniswap.get_pair_address( self.token_systemcoin.address, self.token_weth.address)) current_liquidity = self.systemcoin_eth_uniswap.get_current_liquidity() total_liquidity = self.systemcoin_eth_uniswap.get_total_liquidity() systemcoin_exchange_balance = self.systemcoin_eth_uniswap.get_exchange_balance( self.token_systemcoin, self.systemcoin_eth_uniswap.pair_address) weth_exchange_balance = self.systemcoin_eth_uniswap.get_exchange_balance( self.token_weth, self.systemcoin_eth_uniswap.pair_address) # then assert current_liquidity > Wad.from_number(0) assert total_liquidity > Wad.from_number(0) assert total_liquidity > current_liquidity # given amount_a_min = current_liquidity * weth_exchange_balance / total_liquidity amount_b_min = current_liquidity * systemcoin_exchange_balance / total_liquidity remove_liquidity_eth_args = { "liquidity": current_liquidity, "amountBMin": amount_b_min, "amountAMin": amount_a_min } # when remove_liquidity = self.systemcoin_eth_uniswap.remove_liquidity_eth( remove_liquidity_eth_args, self.token_systemcoin, 0).transact(from_address=self.our_address) # then assert remove_liquidity.successful == True assert self.systemcoin_eth_uniswap.get_current_liquidity( ) == Wad.from_number(0) def test_tokens_swap(self): # given add_liquidity = self.add_liquidity_tokens() balance_systemcoin_before_swap = self.systemcoin_usdc_uniswap.get_account_token_balance( self.token_systemcoin) balance_usdc_before_swap = self.systemcoin_usdc_uniswap.get_account_token_balance( self.token_usdc) # when swap = self.systemcoin_usdc_uniswap.swap_exact_tokens_for_tokens( Wad.from_number(.2), self.token_usdc.unnormalize_amount(Wad.from_number(.01)), [self.ds_systemcoin.address.address, self.ds_usdc.address.address ]).transact(from_address=self.our_address) # then assert swap.successful == True balance_systemcoin_after_swap = self.systemcoin_usdc_uniswap.get_account_token_balance( self.token_systemcoin) balance_usdc_after_swap = self.systemcoin_usdc_uniswap.get_account_token_balance( self.token_usdc) assert balance_systemcoin_after_swap < balance_systemcoin_before_swap assert balance_usdc_before_swap < balance_usdc_after_swap
def get_account_token_balance(self, token: Token) -> Wad: assert (isinstance(token, Token)) return token.normalize_amount( ERC20Token(web3=self.web3, address=token.address).balance_of(self.account_address))
WETH_KOVAN_ADDRESS = Address("0xd0a1e359811322d97991e03f863a0c30c2cf029c") DAI_ADDRESS = Address("0x6B175474E89094C44Da98b954EedeAC495271d0F") DAI_KOVAN_ADDRESS = Address("0x4f96fe3b7a6cf9725f59d353f723c1bdb64ca6aa") USDC_ADDRESS = Address("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") MKR_ADDRESS = Address("0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2") MKR_KOVAN_ADDRESS = Address("0xAaF64BFCC32d0F15873a02163e7E500671a4ffcD") FACTORY_ADDRESS = Address("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f") ROUTER_ADDRESS = Address("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D") web3 = Web3(HTTPProvider(sys.argv[1], request_kwargs={"timeout": 600})) web3.eth.defaultAccount = sys.argv[2] register_key(web3, sys.argv[3]) Transact.gas_estimate_for_bad_txs = 210000 dai = Token("DAI", DAI_KOVAN_ADDRESS, 18) weth_kovan = Token("WETH", WETH_KOVAN_ADDRESS, 18) weth_mainnet = Token("WETH", WETH_ADDRESS, 18) usdc_kovan = Token("USDC", USDC_KOVAN_ADDRESS, 6) usdc_mainnet = Token("USDC", USDC_MAINNET_ADDRESS, 6) wbtc = Token('WBTC', Address("0xe0c9275e44ea80ef17579d33c55136b7da269aeb"), 8) uniswap = UniswapV2(web3, dai, weth_kovan) dai_weth_pair = Token("POOL", uniswap.get_pair_address(DAI_KOVAN_ADDRESS, WETH_KOVAN_ADDRESS), 18) # uniswap.approve(wbtc, web3.toWei(5, 'ether')) # uniswap.approve(weth, web3.toWei(5, 'ether')) # amounts_in = uniswap.get_amounts_in(web3.toWei(0.5, 'ether'), [DAI_KOVAN_ADDRESS.address, MKR_KOVAN_ADDRESS.address])
def setup_class(self): self.token = Token( "COW", Address('0xbeef00000000000000000000000000000000BEEF'), 4)
def create_almost_risky_safe(geb: GfDeployment, c: Collateral, collateral_amount: Wad, auction_income_recipient_address: Address, draw_system_coin=True) -> SAFE: assert isinstance(geb, GfDeployment) assert isinstance(c, Collateral) assert isinstance(auction_income_recipient_address, Address) logging.debug("Creating almost risky safe") print("create_almost_risky") print( f"Liquidation price {geb.safe_engine.collateral_type(c.collateral_type.name).safety_price}" ) print( f"Safety price {geb.safe_engine.collateral_type(c.collateral_type.name).liquidation_price}" ) # Ensure vault isn't already unsafe (if so, this shouldn't be called) safe = geb.safe_engine.safe(c.collateral_type, auction_income_recipient_address) assert is_safe_safe( geb.safe_engine.collateral_type(c.collateral_type.name), safe) # Add collateral to auction_income_recipient vault if necessary c.approve(auction_income_recipient_address) token = Token(c.collateral_type.name, c.collateral.address, c.adapter.decimals()) print( f"collateral_amount={collateral_amount} locked_collateral={safe.locked_collateral}" ) delta_collateral = collateral_amount - safe.locked_collateral if delta_collateral > Wad(0): safe_engine_balance = geb.safe_engine.token_collateral( c.collateral_type, auction_income_recipient_address) balance = token.normalize_amount( c.collateral.balance_of(auction_income_recipient_address)) print( f"before join: delta_collateral={delta_collateral} safe_engine_balance={safe_engine_balance} balance={balance} safe_engine_gap={delta_collateral - safe_engine_balance}" ) if safe_engine_balance < delta_collateral: safe_engine_gap = delta_collateral - safe_engine_balance if balance < safe_engine_gap: if c.collateral_type.name.startswith("ETH"): wrap_eth(geb, auction_income_recipient_address, safe_engine_gap) else: raise RuntimeError("Insufficient collateral balance") amount_to_join = token.unnormalize_amount(safe_engine_gap) if amount_to_join == Wad( 0): # handle dusty balances with non-18-decimal tokens amount_to_join += token.unnormalize_amount(token.min_amount) assert c.adapter.join( auction_income_recipient_address, amount_to_join).transact( from_address=auction_income_recipient_address) safe_engine_balance = geb.safe_engine.token_collateral( c.collateral_type, auction_income_recipient_address) print( f"after join: delta_collateral={delta_collateral} safe_engine_balance={safe_engine_balance} balance={balance} safe_engine_gap={delta_collateral - safe_engine_balance}" ) assert safe_engine_balance >= delta_collateral assert geb.safe_engine.modify_safe_collateralization( c.collateral_type, auction_income_recipient_address, delta_collateral, Wad(0)).transact(from_address=auction_income_recipient_address) # Put auction_income_recipient SAFE at max possible debt delta_debt = max_delta_debt(geb, c, auction_income_recipient_address) - Wad(1) if delta_debt > Wad(0): print( f"Attempting to modify safe collateralization with delta_debt={delta_debt}" ) assert geb.safe_engine.modify_safe_collateralization( c.collateral_type, auction_income_recipient_address, Wad(0), delta_debt).transact(from_address=auction_income_recipient_address) # Draw our Dai, simulating the usual behavior safe = geb.safe_engine.safe(c.collateral_type, auction_income_recipient_address) if draw_system_coin and safe.generated_debt > Wad(0): geb.approve_system_coin(auction_income_recipient_address) assert geb.system_coin_adapter.exit( auction_income_recipient_address, safe.generated_debt).transact( from_address=auction_income_recipient_address) print(f"Exited {safe.generated_debt} System coin from safe") logging.debug("Almost risky safe created")