def test_calcPoolOutGivenSingleIn_base(network, alice_wallet): """Tests calculations with calcPoolOutGivenSingleIn.""" pool = _deployBPool(network, alice_wallet) x = pool.calcPoolOutGivenSingleIn( tokenBalanceIn_base=to_base_18(10.0), tokenWeightIn_base=to_base_18(1.0), poolSupply_base=to_base_18(120.0), totalWeight_base=to_base_18(2.0), tokenAmountIn_base=to_base_18(0.1), swapFee_base=0, ) assert round(from_base_18(x), 3) == 0.599
def test_calcSingleOutGivenPoolIn_base(network, alice_wallet): """Tests pricing with calcSingleOutGivenPoolIn.""" pool = _deployBPool(network, alice_wallet) x = pool.calcSingleOutGivenPoolIn( tokenBalanceOut_base=to_base_18(10.0), tokenWeightOut_base=to_base_18(1.0), poolSupply_base=to_base_18(120.0), totalWeight_base=to_base_18(2.0), poolAmountIn_base=to_base_18(10.0), swapFee_base=0, ) assert round(from_base_18(x), 3) == 1.597
def test_calcInGivenOut_base(network, alice_wallet): """Tests pricing with calcInGivenOut.""" pool = _deployBPool(network, alice_wallet) x = pool.calcInGivenOut( tokenBalanceIn_base=to_base_18(10.0), tokenWeightIn_base=to_base_18(1.0), tokenBalanceOut_base=to_base_18(10.1), tokenWeightOut_base=to_base_18(1.0), tokenAmountOut_base=to_base_18(1.0), swapFee_base=0, ) assert round(from_base_18(x), 3) == 1.099
def calcSingleOutGivenPoolIn(self, pool_address: str, token_out_address: str, pool_shares: float): pool = BPool(pool_address) return from_base_18( pool.calcSingleInGivenPoolOut( pool.getBalance(token_out_address), pool.getDenormalizedWeight(token_out_address), pool.totalSupply(), pool.getTotalDenormalizedWeight(), to_base_18(pool_shares), pool.getSwapFee(), ))
def test_calcPoolInGivenSingleOut_base(network, alice_wallet): """Tests calculations with calcPoolInGivenSingleOut.""" pool = _deployBPool(network, alice_wallet) x = pool.calcPoolInGivenSingleOut( tokenBalanceOut_base=to_base_18(1000.0), tokenWeightOut_base=to_base_18(5.0), poolSupply_base=to_base_18(100.0), totalWeight_base=to_base_18(10.0), tokenAmountOut_base=to_base_18(0.1), swapFee_base=0, ) assert round(from_base_18(x), 3) == 0.005
def calcPoolOutGivenSingleIn(self, pool_address: str, token_in_address: str, token_in_amount: float): pool = BPool(pool_address) return from_base_18( pool.calcPoolOutGivenSingleIn( pool.getBalance(token_in_address), pool.getDenormalizedWeight(token_in_address), pool.totalSupply(), pool.getTotalDenormalizedWeight(), to_base_18(token_in_amount), pool.getSwapFee(), ))
def get_token_price(self, pool_address: str) -> float: """ :param pool_address: str the address of the pool contract :return: int price of data token in terms of OCEAN tokens """ dtoken_address = self.get_token_address(pool_address) pool = BPool(pool_address) return from_base_18( pool.getSpotPrice( tokenIn_address=self.ocean_address, tokenOut_address=dtoken_address ) )
def test_2tokens_basic(network, T1, T2, alice_wallet, alice_address): pool = _deployBPool(network, alice_wallet) assert T1.address != T2.address assert T1.address != pool.address assert from_base_18(T1.balanceOf(alice_address)) >= 90.0 _ = from_base_18(T2.balanceOf(alice_address)) >= 10.0 with pytest.raises(Exception): # can't bind until we approve pool.bind( T1.address, to_base_18(90.0), to_base_18(9.0), from_wallet=alice_wallet ) # Bind two tokens to the pool T1.approve(pool.address, to_base_18(90.0), from_wallet=alice_wallet) T2.approve(pool.address, to_base_18(10.0), from_wallet=alice_wallet) assert from_base_18(T1.allowance(alice_address, pool.address)) == 90.0 assert from_base_18(T2.allowance(alice_address, pool.address)) == 10.0 assert not pool.isBound(T1.address) and not pool.isBound(T1.address) pool.bind(T1.address, to_base_18(90.0), to_base_18(9.0), from_wallet=alice_wallet) pool.bind(T2.address, to_base_18(10.0), to_base_18(1.0), from_wallet=alice_wallet) assert pool.isBound(T1.address) and pool.isBound(T2.address) assert pool.getNumTokens() == 2 assert pool.getCurrentTokens() == [T1.address, T2.address] assert pool.getDenormalizedWeight(T1.address) == to_base_18(9.0) assert pool.getDenormalizedWeight(T2.address) == to_base_18(1.0) assert pool.getTotalDenormalizedWeight() == to_base_18(9.0 + 1.0) assert pool.getNormalizedWeight(T1.address) == to_base_18(0.9) assert pool.getNormalizedWeight(T2.address) == to_base_18(0.1) assert pool.getBalance(T1.address) == to_base_18(90.0) assert pool.getBalance(T2.address) == to_base_18(10.0) assert str(pool)
def get_liquidity_history(poolAddress): """ :param poolAddress: :return: json object with two keys: `ocean` and `datatoken` each has a list of datapoints sampled at specific time intervals from the pools liquidity history. """ try: result = dict() ocean = Ocean(ConfigProvider.get_config()) pool = BPool(poolAddress) dt_address = ocean.pool.get_token_address(poolAddress, pool, validate=False) swap_fee = from_base_18(pool.getSwapFee()) ocn_weight = from_base_18( pool.getDenormalizedWeight(ocean.OCEAN_address)) dt_weight = from_base_18(pool.getDenormalizedWeight(dt_address)) ocn_add_remove_list, dt_add_remove_list = ocean.pool.get_liquidity_history( poolAddress) ocn_add_remove_list = [(v, int(t)) for v, t in ocn_add_remove_list] dt_add_remove_list = [(v, int(t)) for v, t in dt_add_remove_list] ocn_reserve_history, dt_reserve_history, price_history = build_liquidity_and_price_history( ocn_add_remove_list, dt_add_remove_list, ocn_weight, dt_weight, swap_fee) result['oceanAddRemove'] = ocn_add_remove_list result['datatokenAddRemove'] = dt_add_remove_list result['oceanReserveHistory'] = ocn_reserve_history result['datatokenReserveHistory'] = dt_reserve_history result['datatokenPriceHistory'] = price_history return Response(json.dumps(result), 200, content_type='application/json') except Exception as e: logger.error(f'pools/history/{poolAddress}: {str(e)}', exc_info=1) return f'Get pool liquidity/price history failed: {str(e)}', 500
def test_joinSwapExternAmountIn(network, T1, T2, alice_wallet, alice_address): """Tests adding an external amount inside a pool. When the pool is not public, assert that an Exception is thrown. When the pool is public, assert that the swap is made and the correct balance remains. """ init_T1balance = from_base_18(T1.balanceOf(alice_address)) T2balance = from_base_18(T2.balanceOf(alice_address)) pool = _createPoolWith2Tokens(network, T1, T2, alice_wallet, 90.0, 10.0, 9.0, 1.0) T1.approve(pool.address, to_base_18(100.0), from_wallet=alice_wallet) # pool's not public with pytest.raises(Exception): pool.swapExactAmountOut( tokenIn_address=T1.address, maxAmountIn_base=to_base_18(100.0), tokenOut_address=T2.address, tokenAmountOut_base=to_base_18(10.0), maxPrice_base=HUGEINT, from_wallet=alice_wallet, ) # pool's public pool.setPublicSwap(True, from_wallet=alice_wallet) pool.swapExactAmountOut( tokenIn_address=T1.address, maxAmountIn_base=to_base_18(100.0), tokenOut_address=T2.address, tokenAmountOut_base=to_base_18(1.0), maxPrice_base=HUGEINT, from_wallet=alice_wallet, ) new_balance = init_T1balance - 91.055 assert ((new_balance - 0.005) <= from_base_18(T1.balanceOf(alice_address)) <= (new_balance + 0.005)) assert from_base_18(T2.balanceOf(alice_address)) == (T2balance - 9.0)
def _get_fixedrateexchange_price(self, dt_address, owner=None, exchange_id=None): fre = self.ex_contract try: if not exchange_id: assert owner is not None, 'owner is required when `exchange_id` is not given.' exchange_id = add_0x_prefix( fre.generateExchangeId(self._checksum_ocean, dt_address, owner).hex()) ex_data = fre.getExchange(exchange_id) if not ex_data or not ex_data.exchangeOwner: return None, None price = from_base_18(ex_data.fixedRate) supply = from_base_18(ex_data.supply) return price, supply except Exception as e: logger.error( f'Reading exchange price failed for datatoken {dt_address}, ' f'owner {owner}, exchangeId {exchange_id}: {e}') return None, None
def calculate_token_holders( self, from_block: int, to_block: int, min_token_amount: float ) -> List[Tuple[str, float]]: """Returns a list of addresses with token balances above a minimum token amount. Calculated from the transactions between `from_block` and `to_block`.""" all_transfers, _ = self.get_all_transfers_from_events(from_block, to_block) balances_above_threshold = [] balances = DataToken.calculate_balances(all_transfers) _min = to_base_18(min_token_amount) balances_above_threshold = sorted( [(a, from_base_18(b)) for a, b in balances.items() if b > _min], key=lambda x: x[1], reverse=True, ) return balances_above_threshold
def test_ERC20(alice_ocean, alice_wallet, alice_address, bob_wallet, bob_address): """Tests DataToken minting, allowance and transfer.""" token = alice_ocean.create_data_token("DataToken1", "DT1", from_wallet=alice_wallet, blob="foo_blob") assert token.datatoken_name() == "DataToken1" assert token.symbol()[:2] == "DT" assert token.decimals() == 18 assert token.balanceOf(alice_address) == 0 assert token.totalSupply() == 0 token.mint(alice_address, to_base_18(100.0), from_wallet=alice_wallet) assert from_base_18(token.balanceOf(alice_address)) == 100.0 assert token.allowance(alice_address, bob_address) == 0 token.approve(bob_address, to_base_18(1.0), from_wallet=alice_wallet) assert token.allowance(alice_address, bob_address) == int(1e18) token.increaseAllowance(bob_address, to_base_18(1.0), from_wallet=alice_wallet) assert token.allowance(alice_address, bob_address) == int(2e18) token.decreaseAllowance(bob_address, to_base_18(1.0), from_wallet=alice_wallet) assert token.allowance(alice_address, bob_address) == int(1e18) token.transferFrom(alice_address, bob_address, to_base_18(1.0), from_wallet=bob_wallet) assert from_base_18(token.balanceOf(alice_address)) == 99.0 assert from_base_18(token.balanceOf(bob_address)) == 1.0 token.transfer(bob_address, to_base_18(5.0), from_wallet=alice_wallet) assert from_base_18(token.balanceOf(alice_address)) == 94.0 assert from_base_18(token.balanceOf(bob_address)) == 6.0 token.transfer(alice_address, to_base_18(3.0), from_wallet=bob_wallet) assert from_base_18(token.balanceOf(alice_address)) == 97.0 assert from_base_18(token.balanceOf(bob_address)) == 3.0 # assert transfers were successful block = alice_ocean.web3.eth.blockNumber all_transfers = token.get_all_transfers_from_events(block - 2, block + 1, chunk=1) assert len(all_transfers[0]) == 3
def calcOutGivenIn( self, pool_address: str, token_in_address: str, token_out_address: str, token_in_amount: float, ): pool = BPool(pool_address) out_amount = pool.calcOutGivenIn( pool.getBalance(token_in_address), pool.getDenormalizedWeight(token_in_address), pool.getBalance(token_out_address), pool.getDenormalizedWeight(token_out_address), to_base_18(token_in_amount), pool.getSwapFee(), ) return from_base_18(out_amount)
def test_status_functions(alice_ocean, alice_wallet, alice_address): """Tests various status functions of the DataToken class.""" token = alice_ocean.create_data_token("DataToken1", "DT1", from_wallet=alice_wallet, blob="foo_blob") token.mint(alice_address, to_base_18(100.0), from_wallet=alice_wallet) assert from_base_18(token.balanceOf(alice_address)) == 100.0 assert token.totalSupply() == 100_000_000_000_000_000_000 assert token.cap() == 1_000_000_000_000_000_000_000 assert token.datatoken_name() == "DataToken1" assert token.minter() == alice_address assert token.isMinter(alice_address) with pytest.raises(ValueError): token.get_event_signature("not a registered event")
def _get_liquidity_and_price(self, pools, dt_address): assert pools, f'pools should not be empty, got {pools}' logger.debug(f' Searching {pools} for {dt_address}') dt_address_lower = dt_address.lower() pool_to_price = dict() for _pool in pools: try: pool = BPool(_pool) try: ptokens = {a.lower() for a in pool.getCurrentTokens()} except Exception: continue if self._OCEAN not in ptokens or dt_address_lower not in ptokens: logger.debug( f' ignore pool {_pool}, cannot find {self._OCEAN} and {dt_address_lower} in tokens list {ptokens}' ) continue price = from_base_18( pool.getSpotPrice(self._checksum_ocean, dt_address)) if price <= 0.0 or price > self.PRICE_TOO_LARGE: continue pool_to_price[_pool] = price logger.debug(f' Adding pool {_pool} with price {price}') except Exception as e: logger.error( f'failed to get liquidity/price info from pool {_pool} and datatoken {dt_address}: {e}' ) if pool_to_price: _pool = sorted(pool_to_price.items(), key=lambda x: x[1])[0][0] dt_reserve, ocn_reserve, price, _pool = self.get_pool_reserves_and_price( _pool, dt_address) return dt_reserve, ocn_reserve, price, _pool # no pool or no pool with price was found return 0.0, 0.0, 0.0, pools[0]
def test_joinswapPoolAmountOut(network, T1, T2, alice_address, alice_wallet): T1balance = from_base_18(T1.balanceOf(alice_address)) pool = _createPoolWith2Tokens(network, T1, T2, alice_wallet, 90.0, 10.0, 9.0, 1.0) BPT = pool pool.finalize(from_wallet=alice_wallet) pool_balance = from_base_18(BPT.balanceOf(alice_address)) T1.approve(pool.address, to_base_18(90.0), from_wallet=alice_wallet) assert from_base_18(T1.balanceOf(alice_address)) == (T1balance - 90) T1balance = from_base_18(T1.balanceOf(alice_address)) pool.joinswapPoolAmountOut( tokenIn_address=T1.address, poolAmountOut_base=to_base_18(10.0), # BPT wanted maxAmountIn_base=to_base_18(90.0), # max T1 to spend from_wallet=alice_wallet, ) assert from_base_18(T1.balanceOf(alice_address)) >= (T1balance - 90.0) assert from_base_18(BPT.balanceOf(alice_address)) == (pool_balance + 10.0)
def test_status_functions(alice_ocean, alice_wallet, alice_address): """Tests various status functions of the DataToken class.""" token = alice_ocean.create_data_token( "DataToken1", "DT1", from_wallet=alice_wallet, blob="foo_blob" ) token.mint(alice_address, to_base_18(100.0), from_wallet=alice_wallet) assert from_base_18(token.balanceOf(alice_address)) == 100.0 assert token.totalSupply() == 100_000_000_000_000_000_000 assert token.cap() == 1_000_000_000_000_000_000_000 assert token.datatoken_name() == "DataToken1" block = alice_ocean.web3.eth.blockNumber token_info = token.get_info( alice_ocean.web3, from_block=(block - 1), to_block=(block + 1), include_holders=True, ) assert len(token_info) == 11 assert token_info["totalSupply"] == 100 with pytest.raises(ValueError): token.get_event_signature("not a registered event")
def get_all_pools(self, from_block=0, chunk_size=1000, include_balance=False): web3 = Web3Provider.get_web3() current_block = web3.eth.blockNumber bfactory = BFactory(self.bfactory_address) logs = bfactory.get_event_logs('BPoolRegistered', from_block, current_block, {}, web3=web3, chunk_size=chunk_size) if include_balance: pools = sorted([(l.args.bpoolAddress, from_base_18( BPool(l.args.bpoolAddress).getBalance( self.ocean_address))) for l in logs], key=lambda x: x[1], reverse=True) else: pools = {l.args.bpoolAddress for l in logs} return pools
def get_quote(self, amount: float, exchange_id: str): exchange = self._exchange_contract() amount_base = to_base_18(amount) ocean_amount_base = exchange.get_base_token_quote( exchange_id, amount_base) return from_base_18(ocean_amount_base)
def test_fixed_rate_exchange( alice_ocean, alice_wallet, T1, bob_wallet, T2, contracts_addresses ): """ tests: create generateExchangeId getExchange getExchanges getNumberOfExchanges get_base_token_quote buy_data_token """ base_unit = to_base_18(1.0) fixed_ex = FixedRateExchange(contracts_addresses[FixedRateExchange.CONTRACT_NAME]) num_ex = fixed_ex.getNumberOfExchanges() assert num_ex == len( fixed_ex.getExchanges() ), "num exchanges do not match num of exchange ids." ocean_t = alice_ocean.OCEAN_address ocn_token = DataToken(ocean_t) # owner_wallet = get_ganache_wallet() # ocn_token.transfer_tokens(bob_wallet.address, 100, owner_wallet) assert ocn_token.token_balance(bob_wallet.address) >= 100, ( f"bob wallet does not have the expected OCEAN tokens balance, " f"got {ocn_token.token_balance(bob_wallet.address)} instead of 100" ) # clear any previous ocean token allowance for the exchange contract assert ( ocn_token.get_tx_receipt( ocn_token.approve(fixed_ex.address, 1, bob_wallet) ).status == 1 ), "approve failed" assert ocn_token.allowance(bob_wallet.address, fixed_ex.address) == 1, "" rate = to_base_18(0.1) tx_id = fixed_ex.create(ocean_t, T1.address, rate, alice_wallet) r = fixed_ex.get_tx_receipt(tx_id) assert r.status == 1, f"create fixed rate exchange failed: TxId {tx_id}." ex_id = fixed_ex.generateExchangeId(ocean_t, T1.address, alice_wallet.address).hex() ex_data = fixed_ex.getExchange(ex_id) expected_values = (alice_wallet.address, T1.address, ocean_t, rate, True, 0) assert ex_data == expected_values, ( f"fixed rate exchange {ex_id} with values {ex_data} " f"does not match the expected values {expected_values}" ) assert ( fixed_ex.getNumberOfExchanges() == num_ex + 1 ), f"Number of exchanges does not match, expected {num_ex+1} got {fixed_ex.getNumberOfExchanges()}." ################### # Test quote and buy datatokens amount = to_base_18(10.0) # 10 data tokens base_token_quote = fixed_ex.get_base_token_quote(ex_id, amount) # quote = from_base_18(base_token_quote) assert base_token_quote == ( amount * rate / base_unit ), f"quote does not seem correct: expected {amount*rate/base_unit}, got {base_token_quote}" assert from_base_18(base_token_quote) == 1.0, "" # buy without approving OCEAN tokens, should fail assert ( run_failing_tx(fixed_ex, fixed_ex.buy_data_token, ex_id, amount, bob_wallet) == 0 ), ( f"buy_data_token/swap on EX {ex_id} is expected to fail but did not, " f"maybe the FixedRateExchange is already approved as spender for bob_wallet." ) # approve ocean tokens, buying should still fail because datatokens are not approved by exchange owner assert ( ocn_token.get_tx_receipt( ocn_token.approve(fixed_ex.address, base_token_quote, bob_wallet) ).status == 1 ), "approve failed" assert ( run_failing_tx(fixed_ex, fixed_ex.buy_data_token, ex_id, amount, bob_wallet) == 0 ), ( f"buy_data_token/swap on EX {ex_id} is expected to fail but did not, " f"maybe the FixedRateExchange is already approved as spender for bob_wallet." ) # approve data token, now buying should succeed assert ( T1.get_tx_receipt(T1.approve(fixed_ex.address, amount, alice_wallet)).status == 1 ), "approve failed" assert ( ocn_token.allowance(bob_wallet.address, fixed_ex.address) == base_token_quote ), "" tx_id = fixed_ex.buy_data_token(ex_id, amount, bob_wallet) r = fixed_ex.get_tx_receipt(tx_id) assert ( r.status == 1 ), f"buy_data_token/swap on EX {ex_id} failed with status 0: amount {amount}." # verify bob's datatokens balance assert T1.balanceOf(bob_wallet.address) == amount, ( f"bobs datatoken balance is not right, " f"should be {amount}, got {T1.balanceOf(bob_wallet.address)}" ) assert ocn_token.allowance(bob_wallet.address, fixed_ex.address) == 0, "" ##################### # create another ex then do more tests rate2 = to_base_18(0.8) tx_id = fixed_ex.create(ocean_t, T2.address, rate2, alice_wallet) r = fixed_ex.get_tx_receipt(tx_id) assert r.status == 1, f"create fixed rate exchange failed: TxId {tx_id}." assert fixed_ex.getNumberOfExchanges() == num_ex + 2, ( f"Number of exchanges does not match, " f"expected {num_ex+2} got {fixed_ex.getNumberOfExchanges()}." ) t2_ex_id = fixed_ex.generateExchangeId( ocean_t, T2.address, alice_wallet.address ).hex() exchange_ids = {ti.hex() for ti in fixed_ex.getExchanges()} assert ex_id in exchange_ids, "exchange id not found." assert t2_ex_id in exchange_ids, "exchange id not found." ############################## # test activate/deactivate assert fixed_ex.isActive(ex_id) is True, f"exchange {ex_id} is not active." assert fixed_ex.isActive(t2_ex_id) is True, f"exchange {t2_ex_id} is not active." assert ( run_failing_tx(fixed_ex, fixed_ex.deactivate, t2_ex_id, bob_wallet) == 0 ), f"exchange {t2_ex_id} deactivate (using bob_wallet) should fail but did not." assert ( fixed_ex.get_tx_receipt(fixed_ex.deactivate(t2_ex_id, alice_wallet)).status == 1 ), f"exchange {t2_ex_id} deactivate failed." assert ( fixed_ex.isActive(t2_ex_id) is False ), f"exchange {t2_ex_id} is active, but it should be deactivated." ################################### # try buying from deactivated ex amount = to_base_18(4.0) # num data tokens base_token_quote = fixed_ex.get_base_token_quote( t2_ex_id, amount ) # num base token (OCEAN tokens assert base_token_quote == ( amount * rate2 / base_unit ), f"quote does not seem correct: expected {amount*rate2/base_unit}, got {base_token_quote}" ocn_token.get_tx_receipt( ocn_token.approve(fixed_ex.address, base_token_quote, bob_wallet) ) # buy should fail (deactivated exchange) assert ( run_failing_tx(fixed_ex, fixed_ex.buy_data_token, t2_ex_id, amount, bob_wallet) == 0 ), ( f"buy_data_token/swap on EX {t2_ex_id} is expected to fail but did not, " f"maybe the FixedRateExchange is already approved as spender for bob_wallet." ) assert ( ocn_token.allowance(bob_wallet.address, fixed_ex.address) == base_token_quote ), "" assert ( fixed_ex.get_tx_receipt(fixed_ex.activate(t2_ex_id, alice_wallet)).status == 1 ), f"exchange {t2_ex_id} deactivate failed." assert ( fixed_ex.isActive(t2_ex_id) is True ), f"exchange {t2_ex_id} is not active, but it should be." ############################## # buy should still fail as datatokens are not approved to spend by the exchange contract assert ( run_failing_tx(fixed_ex, fixed_ex.buy_data_token, t2_ex_id, amount, bob_wallet) == 0 ), ( f"buy_data_token/swap on EX {t2_ex_id} is expected to fail but did not, " f"maybe the FixedRateExchange is already approved as spender for bob_wallet." ) # now buy tokens should succeed assert ( T2.get_tx_receipt(T2.approve(fixed_ex.address, amount * 3, alice_wallet)).status == 1 ), "approve failed" assert ( fixed_ex.get_tx_receipt( fixed_ex.buy_data_token(t2_ex_id, amount, bob_wallet) ).status == 1 ), f"buy_data_token/swap on EX {ex_id} failed, " assert ocn_token.allowance(bob_wallet.address, fixed_ex.address) == 0, "" # approve again for another purchase ocn_token.get_tx_receipt( ocn_token.approve(fixed_ex.address, base_token_quote, bob_wallet) ) assert ( run_failing_tx( fixed_ex, fixed_ex.buy_data_token, t2_ex_id, to_base_18(5.0), bob_wallet ) == 0 ), f"buy_data_token/swap on EX {t2_ex_id} should fail because not enough Ocean tokens are approved by buyer." # get new quote for new amount base_token_quote = fixed_ex.get_base_token_quote( t2_ex_id, to_base_18(5.0) ) # num base token (OCEAN tokens ocn_token.get_tx_receipt( ocn_token.approve(fixed_ex.address, base_token_quote, bob_wallet) ) assert ( fixed_ex.get_tx_receipt( fixed_ex.buy_data_token(t2_ex_id, to_base_18(5.0), bob_wallet) ).status == 1 ), f"buy_data_token/swap on EX {t2_ex_id} failed." assert ocn_token.allowance(bob_wallet.address, fixed_ex.address) == 0, "" ############################## # test getRate/setRate assert ( fixed_ex.getRate(t2_ex_id) == rate2 ), f"T2 exchange rate does not match {rate2}, got {fixed_ex.getRate(t2_ex_id)}" assert ( fixed_ex.getRate(ex_id) == rate ), f"T1 exchange rate does not match {rate}, got {fixed_ex.getRate(ex_id)}" rate2 = to_base_18(0.75) assert ( fixed_ex.get_tx_receipt(fixed_ex.setRate(t2_ex_id, rate2, alice_wallet)).status == 1 ), "setRate failed." assert ( fixed_ex.getRate(t2_ex_id) == rate2 ), f"T2 exchange rate does not match {rate2}, got {fixed_ex.getRate(t2_ex_id)}" assert ( run_failing_tx( fixed_ex, fixed_ex.setRate, t2_ex_id, to_base_18(0.0), alice_wallet ) == 0 ), "should not accept rate of Zero." assert ( run_failing_tx( fixed_ex, fixed_ex.setRate, t2_ex_id, -to_base_18(0.05), alice_wallet ) == 0 ), "should not accept a negative rate." assert ( fixed_ex.get_tx_receipt( fixed_ex.setRate(t2_ex_id, to_base_18(1000.0), alice_wallet) ).status == 1 ), "setRate failed."
def get_pool_info(self, pool_address, dt_address=None, from_block=None, to_block=None, flags=None): if not flags: flags = self.POOL_INFO_FLAGS from18 = from_base_18 web3 = Web3Provider.get_web3() current_block = to_block if to_block is not None else web3.eth.blockNumber # RPC_CALL pool = BPool(pool_address) dt_address = dt_address if dt_address else self.get_token_address( pool_address, pool, validate=False) # RPC_CALL from_block = from_block if from_block is not None else self.get_creation_block( pool_address) # RPC_CALL pool_creator = None shares = None info_dict = {'address': pool.address, 'dataTokenAddress': dt_address} if 'datatokenInfo' in flags: info_dict['dataToken'] = DataToken(dt_address).get_info( web3, from_block, current_block, include_holders=bool('dtHolders' in flags)) if 'price' in flags: info_dict.update({ 'spotPrice1DT': from18(pool.getSpotPrice(self.ocean_address, dt_address)), 'totalPrice1DT': self.getOceanRequiredToBuyDT(pool_address, dt_amount=1.0), }) if 'reserve' in flags: ocn_reserve = from18(pool.getBalance(self.ocean_address)) dt_reserve = from18(pool.getBalance(dt_address)) info_dict.update({ 'oceanWeight': from18(pool.getDenormalizedWeight(self.ocean_address)), 'oceanReserve': ocn_reserve, 'dtWeight': from18(pool.getDenormalizedWeight(dt_address)), 'dtReserve': dt_reserve, }) if 'shares' in flags or 'creator' in flags: pool_creator = pool.getController() shares = from18(pool.totalSupply()) info_dict.update({ 'creator': pool_creator, }) if 'shareHolders' in flags: pool_erc20 = DataToken(pool_address) all_transfers, block = pool_erc20.get_all_transfers_from_events( from_block, current_block) a_to_balance = DataToken.calculate_balances(all_transfers) _min = to_base_18(0.001) pool_holders = sorted( [(a, from_base_18(b)) for a, b in a_to_balance.items() if b > _min], key=lambda x: x[1], reverse=True) info_dict.update({ 'numShareHolders': len(pool_holders), 'shareHolders': pool_holders }) all_join_records = [] all_exit_records = [] if 'liquidityTotals' in flags or 'liquidity' in flags: all_join_records = self.get_all_liquidity_additions( web3, pool_address, from_block, current_block, dt_address, raw_result=False) # RPC_CALL total_ocn_additions = from18( sum(r[2] for r in all_join_records if r[1] == self.ocean_address)) all_exit_records = self.get_all_liquidity_removals( web3, pool_address, from_block, current_block, dt_address, raw_result=False) # RPC_CALL total_ocn_removals = from18( sum(r[2] for r in all_exit_records if r[1] == self.ocean_address)) info_dict.update({ 'totalOceanAdditions': total_ocn_additions, 'totalOceanRemovals': total_ocn_removals, }) if 'liquidity' in flags: creator_shares = from18(pool.balanceOf(pool_creator)) creator_shares_percent = creator_shares / shares account_to_join_record = self.get_account_to_liquidity_records_map( all_join_records) ocean_additions = [ from18(r[2]) for r in account_to_join_record[pool_creator] if r[1] == self.ocean_address ] dt_additions = [ from18(r[2]) for r in account_to_join_record[pool_creator] if r[1] == dt_address ] account_to_exit_record = self.get_account_to_liquidity_records_map( all_exit_records) ocean_removals = [ from18(r[2]) for r in account_to_exit_record.get(pool_creator, []) if r[1] == self.ocean_address ] dt_removals = [ from18(r[2]) for r in account_to_exit_record.get(pool_creator, []) if r[1] == dt_address ] all_swap_records = self.get_all_swaps(web3, pool_address, from_block, current_block, dt_address, raw_result=False) account_to_swap_record = self.get_account_to_liquidity_records_map( all_swap_records) ocean_in = [ from18(r[2]) for r in account_to_swap_record.get(pool_creator, []) if r[1] == self.ocean_address ] dt_in = [ from18(r[2]) for r in account_to_swap_record.get(pool_creator, []) if r[1] == dt_address ] ocean_out = [ from18(r[4]) for r in account_to_swap_record.get(pool_creator, []) if r[3] == self.ocean_address ] dt_out = [ from18(r[4]) for r in account_to_swap_record.get(pool_creator, []) if r[3] == dt_address ] swap_fee = from18(pool.getSwapFee()) sum_ocean_additions = sum(ocean_additions) sum_ocean_removals = sum(ocean_removals) sum_ocn_swap_in = sum(ocean_in) sum_ocn_swap_out = sum(ocean_out) sum_dt_additions = sum(dt_additions) sum_dt_removals = sum(dt_removals) sum_dt_swap_in = sum(dt_in) sum_dt_swap_out = sum(dt_out) taxable_ocn = sum_ocn_swap_in + sum_ocn_swap_out + sum_ocean_additions + sum_ocean_removals - ocean_additions[ 0] taxable_dt = sum_dt_swap_in + sum_dt_swap_out + sum_dt_additions + sum_dt_removals - dt_additions[ 0] info_dict.update({ 'totalShares': shares, 'creator': pool_creator, 'creatorShares': creator_shares, 'creatorSharesPercentage': creator_shares_percent, 'creatorFirstOceanStake': ocean_additions[0], 'creatorFirstDTStake': dt_additions[0], 'creatorTotalOceanStake': sum(ocean_additions), 'creatorTotalDTStake': sum(dt_additions), 'creatorTotalOceanUnstake': sum(ocean_removals), 'creatorTotalDTUnstake': sum(dt_removals), 'totalOceanSwapIn': sum_ocn_swap_in, 'totalOceanSwapOut': sum_ocn_swap_out, 'totalDTSwapIn': sum_dt_swap_in, 'totalDTSwapOut': sum_dt_swap_out, 'totalSwapFeesDT': swap_fee * taxable_dt, 'totalSwapFeesOcean': swap_fee * taxable_ocn, }) info_dict.update({ 'fromBlockNumber': from_block, 'latestBlockNumber': current_block }) return info_dict
def getReserve(self, pool_address: str, token_address: str): return from_base_18(BPool(pool_address).getBalance(token_address))
def test_public_pool(network, bob_wallet): alice = alice_info() alice_address = alice.address alice_wallet = alice.wallet bob_address = bob_wallet.address T1 = alice.T1 T2 = alice.T2 pool = _createPoolWith2Tokens(network, alice.T1, alice.T2, alice.wallet, 90.0, 10.0, 9.0, 1.0) BPT = pool web3 = alice.wallet.web3 #alice give Bob some tokens alice.T1.transfer(bob_wallet.address, to_base_18(100.0), from_wallet=alice.wallet) alice.T2.transfer(bob_wallet.address, to_base_18(100.0), from_wallet=alice.wallet) #verify holdings assert from_base_18(alice.T1.balanceOf(alice.address)) == (1000.0 - 90.0 - 100.0) assert from_base_18(alice.T2.balanceOf(alice.address)) == (1000.0 - 10.0 - 100.0) assert from_base_18(BPT.balanceOf(alice.address)) == 0 assert from_base_18(alice.T1.balanceOf(bob_address)) == 100.0 assert from_base_18(alice.T2.balanceOf(bob_address)) == 100.0 assert from_base_18(BPT.balanceOf(bob_address)) == 0 assert from_base_18(T1.balanceOf(pool.address)) == 90.0 assert from_base_18(T2.balanceOf(pool.address)) == 10.0 assert from_base_18(BPT.balanceOf(pool.address)) == 0 #finalize pool = BPool(pool.address) pool.finalize(from_wallet=alice.wallet) #verify holdings assert from_base_18(alice.T1.balanceOf(alice.address)) == (1000.0 - 90.0 - 100.0) assert from_base_18(alice.T2.balanceOf(alice.address)) == (1000.0 - 10.0 - 100.0) assert from_base_18(BPT.balanceOf(alice.address)) == 100.0 #new! assert from_base_18(T1.balanceOf(pool.address)) == 90.0 assert from_base_18(T2.balanceOf(pool.address)) == 10.0 assert from_base_18(BPT.balanceOf(pool.address)) == 0 #bob join pool. Wants 10 BPT T1.approve(pool.address, to_base_18(100.0), from_wallet=bob_wallet) T2.approve(pool.address, to_base_18(100.0), from_wallet=bob_wallet) pool.joinPool( poolAmountOut_base=to_base_18(10.0), #10 BPT maxAmountsIn_base=[to_base_18(100.0), to_base_18(100.0)], from_wallet=bob_wallet) #verify holdings assert from_base_18(T1.balanceOf(alice_address)) == (1000.0 - 90.0 - 100.0) assert from_base_18(T2.balanceOf(alice_address)) == (1000.0 - 10.0 - 100.0) assert from_base_18(BPT.balanceOf(alice_address)) == 100.0 assert from_base_18(T1.balanceOf(bob_address)) == (100.0 - 9.0) assert from_base_18(T2.balanceOf(bob_address)) == (100.0 - 1.0) assert from_base_18(BPT.balanceOf(bob_address)) == 10.0 assert from_base_18(T1.balanceOf(pool.address)) == (90.0 + 9.0) assert from_base_18(T2.balanceOf(pool.address)) == (10.0 + 1.0) assert from_base_18(BPT.balanceOf(pool.address)) == 0 #bob sells 2 BPT # -this is where BLabs fee kicks in. But the fee is currently set to 0. pool.exitPool(poolAmountIn_base=to_base_18(2.0), minAmountsOut_base=[to_base_18(0.0), to_base_18(0.0)], from_wallet=bob_wallet) assert from_base_18(T1.balanceOf(bob_address)) == 92.8 assert from_base_18(T2.balanceOf(bob_address)) == 99.2 assert from_base_18(BPT.balanceOf(bob_address)) == 8.0 #bob buys 5 more BPT pool.joinPool(poolAmountOut_base=to_base_18(5.0), maxAmountsIn_base=[to_base_18(90.0), to_base_18(90.0)], from_wallet=bob_wallet) assert from_base_18(BPT.balanceOf(bob_address)) == 13.0 #bob fully exits pool.exitPool(poolAmountIn_base=to_base_18(13.0), minAmountsOut_base=[to_base_18(0.0), to_base_18(0.0)], from_wallet=bob_wallet) assert from_base_18(BPT.balanceOf(bob_address)) == 0.0
def test_setSwapFee_works(network, alice_wallet): """Tests that a swap fee can be set on the pool by the controller of that pool.""" pool = _deployBPool(network, alice_wallet) pool.setSwapFee(to_base_18(0.011), from_wallet=alice_wallet) assert from_base_18(pool.getSwapFee()) == 0.011
def test_public_pool(network, bob_wallet, alice_ocean): """Tests successful transfers inside a public pool.""" alice = alice_info() alice_address = alice.address bob_address = bob_wallet.address T1 = alice.T1 T2 = alice.T2 pool = _createPoolWith2Tokens(network, alice.T1, alice.T2, alice.wallet, 90.0, 10.0, 9.0, 1.0) BPT = pool # alice give Bob some tokens alice.T1.transfer(bob_wallet.address, to_base_18(100.0), from_wallet=alice.wallet) alice.T2.transfer(bob_wallet.address, to_base_18(100.0), from_wallet=alice.wallet) # verify holdings assert from_base_18(alice.T1.balanceOf(alice.address)) == (1000.0 - 90.0 - 100.0) assert from_base_18(alice.T2.balanceOf(alice.address)) == (1000.0 - 10.0 - 100.0) assert from_base_18(BPT.balanceOf(alice.address)) == 0 assert from_base_18(alice.T1.balanceOf(bob_address)) == 100.0 assert from_base_18(alice.T2.balanceOf(bob_address)) == 100.0 assert from_base_18(BPT.balanceOf(bob_address)) == 0 assert from_base_18(T1.balanceOf(pool.address)) == 90.0 assert from_base_18(T2.balanceOf(pool.address)) == 10.0 assert from_base_18(BPT.balanceOf(pool.address)) == 0 # finalize pool = BPool(pool.address) pool.finalize(from_wallet=alice.wallet) # verify holdings assert from_base_18(alice.T1.balanceOf(alice.address)) == (1000.0 - 90.0 - 100.0) assert from_base_18(alice.T2.balanceOf(alice.address)) == (1000.0 - 10.0 - 100.0) assert from_base_18(BPT.balanceOf(alice.address)) == 100.0 # new! assert from_base_18(T1.balanceOf(pool.address)) == 90.0 assert from_base_18(T2.balanceOf(pool.address)) == 10.0 assert from_base_18(BPT.balanceOf(pool.address)) == 0 # bob join pool. Wants 10 BPT T1.approve(pool.address, to_base_18(100.0), from_wallet=bob_wallet) T2.approve(pool.address, to_base_18(100.0), from_wallet=bob_wallet) pool.joinPool( poolAmountOut_base=to_base_18(10.0), # 10 BPT maxAmountsIn_base=[to_base_18(100.0), to_base_18(100.0)], from_wallet=bob_wallet, ) # verify holdings assert from_base_18(T1.balanceOf(alice_address)) == (1000.0 - 90.0 - 100.0) assert from_base_18(T2.balanceOf(alice_address)) == (1000.0 - 10.0 - 100.0) assert from_base_18(BPT.balanceOf(alice_address)) == 100.0 assert from_base_18(T1.balanceOf(bob_address)) == (100.0 - 9.0) assert from_base_18(T2.balanceOf(bob_address)) == (100.0 - 1.0) assert from_base_18(BPT.balanceOf(bob_address)) == 10.0 assert from_base_18(T1.balanceOf(pool.address)) == (90.0 + 9.0) assert from_base_18(T2.balanceOf(pool.address)) == (10.0 + 1.0) assert from_base_18(BPT.balanceOf(pool.address)) == 0 # bob sells 2 BPT # -this is where BLabs fee kicks in. But the fee is currently set to 0. pool.exitPool( poolAmountIn_base=to_base_18(2.0), minAmountsOut_base=[to_base_18(0.0), to_base_18(0.0)], from_wallet=bob_wallet, ) assert from_base_18(T1.balanceOf(bob_address)) == 92.8 assert from_base_18(T2.balanceOf(bob_address)) == 99.2 assert from_base_18(BPT.balanceOf(bob_address)) == 8.0 # bob buys 5 more BPT pool.joinPool( poolAmountOut_base=to_base_18(5.0), maxAmountsIn_base=[to_base_18(90.0), to_base_18(90.0)], from_wallet=bob_wallet, ) assert from_base_18(BPT.balanceOf(bob_address)) == 13.0 # bob fully exits pool.exitPool( poolAmountIn_base=to_base_18(13.0), minAmountsOut_base=[to_base_18(0.0), to_base_18(0.0)], from_wallet=bob_wallet, ) assert from_base_18(BPT.balanceOf(bob_address)) == 0.0 block = alice_ocean.web3.eth.blockNumber join_log = pool.get_join_logs(alice_ocean.web3, block - 1, block + 1)[0] assert join_log["args"]["tokenIn"] == T1.address
def _spotPrices(network: str, T1: BToken, T2: BToken, wallet: Wallet, bal1: float, bal2: float, w1: float, w2: float): pool = _createPoolWith2Tokens(network, T1, T2, wallet, bal1, bal2, w1, w2) a1, a2 = T1.address, T2.address return (from_base_18(pool.getSpotPrice(a1, a2)), from_base_18(pool.getSpotPriceSansFee(a1, a2)))
def token_balance(self, account: str): return from_base_18(self.balanceOf(account))
def test_setSwapFee_works(network, alice_wallet): pool = _deployBPool(network, alice_wallet) pool.setSwapFee(to_base_18(0.011), from_wallet=alice_wallet) assert from_base_18(pool.getSwapFee()) == 0.011