def buy_data( ocean, private_key, token_address, seller_wallet, min_amount, max_amount, ): """Buy a dataset on the market. Define wallet, verify that there is enough ganache ETH and OCEAN. Create an exchange_id for a new exchange. Args: ocean (): private_key (str): token_address (str): seller_wallet (Wallet): min_amount (float): max_amount (float): Returns: """ wallet = Wallet(ocean.web3, private_key, ocean.config.block_confirmations) assert ocean.web3.eth.get_balance(wallet.address) > 0, 'need ganache ETH' OCEAN_token = BToken(ocean.web3, ocean.OCEAN_address) assert OCEAN_token.balanceOf(wallet.address) > 0, 'need ganache OCEAN' exchange_id = ocean.exchange.create(token_address, to_wei(min_amount), seller_wallet) tx_result = ocean.exchange.buy_at_fixed_rate(to_wei(min_amount), wallet, to_wei(max_amount), exchange_id, token_address, seller_wallet.address) assert tx_result, 'failed buying data tokens at fixed rate.'
def test1(): # ocean instance config = ExampleConfig.get_config() ConfigProvider.set_config(config) Web3Provider.init_web3( provider=get_web3_connection_provider(config.network_url)) ContractHandler.set_artifacts_path(config.artifacts_path) ocean = Ocean(config) OCEAN_address_before = ocean.OCEAN_address # deploy, distribute, etc deploy_fake_OCEAN() # test: OCEAN address should have changed OCEAN_address_after = ocean.OCEAN_address assert OCEAN_address_before != OCEAN_address_after # test: TEST_PRIVATE_KEY{1,2} should each hold OCEAN wallet1 = Wallet(ocean.web3, private_key=os.getenv("TEST_PRIVATE_KEY1")) wallet2 = Wallet(ocean.web3, private_key=os.getenv("TEST_PRIVATE_KEY2")) OCEAN_after = BToken(ocean.OCEAN_address) assert OCEAN_after.balanceOf(wallet1.address) > 0 assert OCEAN_after.balanceOf(wallet2.address) > 0
def sell_data( ocean, private_key, data_token, amount, fixed_price=True, ): """Sell a dataset on the Ocean market. Mint the datatokens. In the create() step below, ganache OCEAN is needed. Finally, Approve the datatoken for sale. Args: ocean (): wallet (): data_token (): amount (): fixed_price (bool): Whether or not to sell the data at a fixed price. Returns: (bool): Returns True if successful. """ wallet = Wallet(ocean.web3, private_key, ocean.config.block_confirmations) data_token.mint(wallet.address, to_wei(amount), wallet) OCEAN_token = BToken(ocean.web3, ocean.OCEAN_address) assert OCEAN_token.balanceOf(wallet.address) > 0, 'need OCEAN' data_token.approve(ocean.exchange._exchange_address, to_wei(amount), wallet) return True
def sell_data_tokens(self, pool_address: str, amount_base: int, min_OCEAN_amount_base: int, from_wallet: Wallet) -> str: """ Sell data tokens into this pool, receive `min_OCEAN_amount_base` of OCEAN tokens. If total income >= min_OCEAN_amount_base - Caller is spending DataTokens, and receiving OCEAN tokens - DataTokens are going into pool, OCEAN tokens are going out of pool The transaction fails if total income does not reach `min_OCEAN_amount_base` :param pool_address: str address of pool contract :param amount_base: int number of data tokens to add to this pool in *base* :param min_OCEAN_amount_base: :param from_wallet: :return: str transaction id/hash """ dtoken_address = self.get_token_address(pool_address) dt = BToken(dtoken_address) dt.approve(pool_address, amount_base, from_wallet=from_wallet) pool = BPool(pool_address) return pool.swapExactAmountIn( tokenIn_address=dtoken_address, # entering pool tokenAmountIn_base=amount_base, # "" tokenOut_address=self.ocean_address, # leaving pool minAmountOut_base=min_OCEAN_amount_base, # "" maxPrice_base=2**255, # here we limit by max_num_OCEAN, not price from_wallet=from_wallet, )
def _add_liquidity( self, pool_address: str, token_address: str, amount_base: int, from_wallet: Wallet, ) -> str: assert amount_base >= 0 if amount_base == 0: return "" pool = BPool(pool_address) token = BToken(token_address) assert token.balanceOf(from_wallet.address) >= amount_base, ( f"Insufficient funds, {amount_base} tokens are required of token address {token_address}, " f"but only a balance of {token.balanceOf(from_wallet.address)} is available." ) tx_id = token.approve(pool_address, amount_base, from_wallet) r = token.get_tx_receipt(tx_id) if not r or r.status != 1: return 0 pool_amount = pool.joinswapExternAmountIn(token_address, amount_base, 0, from_wallet) return pool_amount
def add_liquidity_finalized(self, pool_address: str, bpt_amount_base: int, max_data_token_amount_base: int, max_OCEAN_amount_base: int, from_wallet: Wallet) -> str: """ Add liquidity to a pool that's been finalized. Buy bpt_amount_base tokens from the pool, spending DataTokens and OCEAN tokens as needed and up to the specified maximum amounts. :param pool_address: str address of pool contract :param bpt_amount_base: int number of pool shares to receive for adding the liquidity :param max_data_token_amount_base: int maximum amount of Data tokens to go into the pool :param max_OCEAN_amount_base: int maximum amount of OCEAN tokens to go into the pool :param from_wallet: Wallet instance :return: str transaction id/hash """ assert self._is_valid_pool(pool_address) dt_address = self.get_token_address(pool_address) dt = BToken(dt_address) dt.approve(pool_address, max_data_token_amount_base, from_wallet=from_wallet) OCEAN = BToken(self.ocean_address) OCEAN.approve(pool_address, max_OCEAN_amount_base, from_wallet=from_wallet) pool = BPool(pool_address) return pool.joinPool( bpt_amount_base, [max_data_token_amount_base, max_OCEAN_amount_base], from_wallet=from_wallet)
def _add_liquidity(self, pool_address: str, token_address: str, amount_base: int, from_wallet: Wallet) -> str: assert amount_base >= 0 if amount_base == 0: return '' pool = BPool(pool_address) token = BToken(token_address) assert token.balanceOf(from_wallet.address) >= amount_base, \ f'Insufficient funds, {amount_base} tokens are required of token address {token_address}, ' \ f'but only a balance of {token.balanceOf(from_wallet.address)} is available.' token.approve(pool_address, amount_base, from_wallet) pool_amount = pool.joinswapExternAmountIn(token_address, amount_base, 0, from_wallet) return pool_amount
def __str__(self) -> str: """Formats with attributes as key, value pairs.""" s = [] s += ["BPool:"] s += [f" pool_address={self.address}"] s += [f" controller address = {self.getController()}"] s += [f" isPublicSwap = {self.isPublicSwap()}"] s += [f" isFinalized = {self.isFinalized()}"] swap_fee = from_wei(self.getSwapFee()) s += [" swapFee = %.2f%%" % (swap_fee * 100)] s += [f" numTokens = {self.getNumTokens()}"] cur_addrs = self.getCurrentTokens() cur_symbols = [BToken(self.web3, addr).symbol() for addr in cur_addrs] s += [f" currentTokens (as symbols) = {', '.join(cur_symbols)}"] if self.isFinalized(): final_addrs = self.getFinalTokens() final_symbols = [ BToken(self.web3, addr).symbol() for addr in final_addrs ] s += [f" finalTokens (as symbols) = {final_symbols}"] s += [" is bound:"] for addr, symbol in zip(cur_addrs, cur_symbols): s += [f" {symbol}: {self.isBound(addr)}"] s += [" weights (fromBase):"] for addr, symbol in zip(cur_addrs, cur_symbols): denorm_w = from_wei(self.getDenormalizedWeight(addr)) norm_w = from_wei(self.getNormalizedWeight(addr)) s += [f" {symbol}: denorm_w={denorm_w}, norm_w={norm_w} "] total_denorm_w = from_wei(self.getTotalDenormalizedWeight()) s += [f" total_denorm_w={total_denorm_w}"] s += [" balances (fromBase):"] for addr, symbol in zip(cur_addrs, cur_symbols): balance = self.getBalance(addr) dec = BToken(self.web3, addr).decimals() s += [f" {symbol}: {from_wei(balance, dec)}"] return "\n".join(s)
def _createPoolWith2Tokens( network: str, T1: BToken, T2: BToken, wallet: Wallet, bal1: float, bal2: float, w1: float, w2: float, ): """Helper function to create a basic pool containing 2 tokens.""" pool = _deployBPool(network, wallet) T1.get_tx_receipt( T1.approve(pool.address, to_base_18(bal1), from_wallet=wallet)) T2.get_tx_receipt( T2.approve(pool.address, to_base_18(bal2), from_wallet=wallet)) if pool.isBound(T1.address): pool.unbind(T1.address, wallet) if pool.isBound(T2.address): pool.unbind(T2.address, wallet) pool.bind(T1.address, to_base_18(bal1), to_base_18(w1), from_wallet=wallet) pool.bind(T2.address, to_base_18(bal2), to_base_18(w2), from_wallet=wallet) return pool
def _createPoolWith2Tokens( network: str, config: Config, web3: Web3, T1: BToken, T2: BToken, wallet: Wallet, bal1: Union[Decimal, str, int], bal2: Union[Decimal, str, int], w1: Union[Decimal, str, int], w2: Union[Decimal, str, int], ): """Helper function to create a basic pool containing 2 tokens.""" pool = _deployBPool(web3, config.address_file, network, wallet) T1.get_tx_receipt( web3, T1.approve(pool.address, to_wei(bal1), from_wallet=wallet)) T2.get_tx_receipt( web3, T2.approve(pool.address, to_wei(bal2), from_wallet=wallet)) if pool.isBound(T1.address): pool.unbind(T1.address, wallet) if pool.isBound(T2.address): pool.unbind(T2.address, wallet) pool.bind(T1.address, to_wei(bal1), to_wei(w1), from_wallet=wallet) pool.bind(T2.address, to_wei(bal2), to_wei(w2), from_wallet=wallet) return pool
def sell_data_tokens( self, pool_address: str, amount: int, min_OCEAN_amount: int, from_wallet: Wallet ) -> str: """ Sell data tokens into this pool, receive `min_OCEAN_amount` of OCEAN tokens. If total income >= min_OCEAN_amount - Caller is spending DataTokens, and receiving OCEAN tokens - DataTokens are going into pool, OCEAN tokens are going out of pool The transaction fails if total income does not reach `min_OCEAN_amount` :param pool_address: str address of pool contract :param amount: int number of data tokens to add to this pool :param min_OCEAN_amount: :param from_wallet: :return: str transaction id/hash """ dtoken_address = self.get_token_address(pool_address) dt = BToken(self.web3, dtoken_address) if dt.balanceOf(from_wallet.address) < amount: raise InsufficientBalance("Insufficient funds for selling DataTokens!") if dt.allowance(from_wallet.address, pool_address) < amount: dt.approve(pool_address, amount, from_wallet=from_wallet) pool = BPool(self.web3, pool_address) return pool.swapExactAmountIn( tokenIn_address=dtoken_address, # entering pool tokenAmountIn=amount, # "" tokenOut_address=self.ocean_address, # leaving pool minAmountOut=min_OCEAN_amount, # "" maxPrice=2 ** 255, # here we limit by max_num_OCEAN, not price from_wallet=from_wallet, )
def _add_liquidity( self, pool_address: str, token_address: str, amount: int, from_wallet: Wallet ) -> str: assert amount >= 0 if amount == 0: return "" pool = BPool(self.web3, pool_address) token = BToken(self.web3, token_address) assert token.balanceOf(from_wallet.address) >= amount, ( f"Insufficient funds, {amount} tokens are required of token address {token_address}, " f"but only a balance of {token.balanceOf(from_wallet.address)} is available." ) if token.allowance(from_wallet.address, pool_address) < amount: tx_id = token.approve(pool_address, amount, from_wallet) r = token.get_tx_receipt(self.web3, tx_id) if not r or r.status != 1: raise VerifyTxFailed( f"Approve OCEAN tokens failed, pool was created at {pool_address}" ) pool_amount = pool.joinswapExternAmountIn(token_address, amount, 0, from_wallet) return pool_amount
def test1(network, OCEAN_address, alice_wallet, alice_ocean, alice_address, bob_wallet): bfactory_address = get_bfactory_address(network) # =============================================================== # 1. Alice publishes a dataset (= publishes a datatoken) # For now, you're Alice:) Let's proceed. DT = alice_ocean.create_data_token("DataToken1", "DT1", alice_wallet, blob="localhost:8030") DT_address = DT.address # =============================================================== # 2. Alice hosts the dataset # Do from console: # >> touch /var/mydata/myFolder1/file # >> ENV DT="{'0x1234':'/var/mydata/myFolder1'}" # >> docker run @oceanprotocol/provider-py -e CONFIG=DT # =============================================================== # 3. Alice mints DTs DT.mint(alice_address, to_base_18(1000.0), alice_wallet) # =============================================================== # 4. Alice creates an OCEAN-DT pool (=a Balancer Pool) bfactory = BFactory(bfactory_address) pool_address = bfactory.newBPool(from_wallet=alice_wallet) pool = BPool(pool_address) pool.setPublicSwap(True, from_wallet=alice_wallet) pool.setSwapFee(to_base_18(0.1), from_wallet=alice_wallet) # set 10% fee DT.approve(pool_address, to_base_18(90.0), from_wallet=alice_wallet) pool.bind( DT_address, to_base_18(90.0), balancer_constants.INIT_WEIGHT_DT_BASE, from_wallet=alice_wallet, ) OCEAN_token = BToken(OCEAN_address) txid = OCEAN_token.approve(pool_address, to_base_18(10.0), from_wallet=alice_wallet) r = OCEAN_token.get_tx_receipt(txid) assert r and r.status == 1, f"approve failed, receipt={r}" pool.bind( OCEAN_address, to_base_18(10.0), balancer_constants.INIT_WEIGHT_OCEAN_BASE, from_wallet=alice_wallet, ) # =============================================================== # 5. Alice adds liquidity to pool DT.approve(pool_address, to_base_18(9.0), from_wallet=alice_wallet) pool.rebind( DT_address, to_base_18(90.0 + 9.0), balancer_constants.INIT_WEIGHT_DT_BASE, from_wallet=alice_wallet, ) OCEAN_token.approve(pool_address, to_base_18(1.0), from_wallet=alice_wallet) pool.rebind( OCEAN_address, to_base_18(10.0 + 1.0), to_base_18(balancer_constants.INIT_WEIGHT_OCEAN), from_wallet=alice_wallet, ) # 6. Bob buys a DT from pool OCEAN_token.approve(pool_address, to_base_18(2.0), from_wallet=bob_wallet) pool.swapExactAmountOut( tokenIn_address=OCEAN_address, maxAmountIn_base=to_base_18(2.0), tokenOut_address=DT_address, tokenAmountOut_base=to_base_18(1.0), maxPrice_base=2**255, from_wallet=bob_wallet, ) # =============================================================== # 7. Bob consumes dataset # <don't need to show here> # =============================================================== # 8. Alice removes liquidity pool.rebind( DT_address, to_base_18(90.0 + 9.0 - 2.0), balancer_constants.INIT_WEIGHT_DT_BASE, from_wallet=alice_wallet, ) pool.rebind( OCEAN_address, to_base_18(10.0 + 1.0 - 3.0), balancer_constants.INIT_WEIGHT_OCEAN_BASE, from_wallet=alice_wallet, ) # =============================================================== # 9. Alice sells data tokens DT.approve(pool_address, to_base_18(1.0), from_wallet=alice_wallet) pool.swapExactAmountIn( tokenIn_address=DT_address, tokenAmountIn_base=to_base_18(1.0), tokenOut_address=OCEAN_address, minAmountOut_base=to_base_18(0.0001), maxPrice_base=2**255, from_wallet=alice_wallet, ) # =============================================================== # 10. Alice finalizes pool. Now others can add liquidity. pool.finalize(from_wallet=alice_wallet) # =============================================================== # 11. Bob adds liquidity DT.approve(pool_address, to_base_18(1.0), from_wallet=bob_wallet) OCEAN_token.approve(pool_address, to_base_18(1.0), from_wallet=bob_wallet) pool.joinPool(to_base_18(0.1), [to_base_18(1.0), to_base_18(1.0)], from_wallet=bob_wallet) # =============================================================== # 12. Bob adds liquidity AGAIN DT.approve(pool_address, to_base_18(1.0), from_wallet=bob_wallet) OCEAN_token.approve(pool_address, to_base_18(1.0), from_wallet=bob_wallet) pool.joinPool(to_base_18(0.1), [to_base_18(1.0), to_base_18(1.0)], from_wallet=bob_wallet)
def test_ERC20(network, alice_wallet, alice_address, bob_wallet, bob_address, OCEAN_address): token = BToken(OCEAN_address) token.approve(bob_address, 0, from_wallet=alice_wallet) assert token.symbol() == 'OCEAN' assert token.decimals() == 18 assert token.balanceOf(alice_address) > util.to_base_18(10.0) assert token.balanceOf(bob_address) > util.to_base_18(10.0) assert token.allowance(alice_address, bob_address) == 0 token.approve(bob_address, int(1e18), from_wallet=alice_wallet) assert token.allowance(alice_address, bob_address) == int(1e18) # alice sends all her OCEAN to Bob, then Bob sends it back alice_OCEAN = token.balanceOf(alice_address) bob_OCEAN = token.balanceOf(bob_address) token.transfer(bob_address, alice_OCEAN, from_wallet=alice_wallet) assert token.balanceOf(alice_address) == 0 assert token.balanceOf(bob_address) == (alice_OCEAN + bob_OCEAN) token.transfer(alice_address, alice_OCEAN, from_wallet=bob_wallet) assert token.balanceOf(alice_address) == alice_OCEAN assert token.balanceOf(bob_address) == bob_OCEAN
def test_ERC20(network, alice_wallet, alice_address, bob_wallet, bob_address, OCEAN_address): """Tests an OCEAN token approval, allowance and transfers.""" token = BToken(OCEAN_address) token.approve(bob_address, 0, from_wallet=alice_wallet) # generating ERC20 Tokens, so the symbol is irrelevant assert token.symbol() == "DTT" assert token.decimals() == 18 assert token.balanceOf(alice_address) > util.to_base_18(10.0) assert token.balanceOf(bob_address) > util.to_base_18(10.0) assert token.allowance(alice_address, bob_address) == 0 token.approve(bob_address, int(1e18), from_wallet=alice_wallet) assert token.allowance(alice_address, bob_address) == int(1e18) # alice sends all her OCEAN to Bob, then Bob sends it back alice_OCEAN = token.balanceOf(alice_address) bob_OCEAN = token.balanceOf(bob_address) token.transfer(bob_address, alice_OCEAN, from_wallet=alice_wallet) assert token.balanceOf(alice_address) == 0 assert token.balanceOf(bob_address) == (alice_OCEAN + bob_OCEAN) token.transfer(alice_address, alice_OCEAN, from_wallet=bob_wallet) assert token.balanceOf(alice_address) == alice_OCEAN assert token.balanceOf(bob_address) == bob_OCEAN
def add_liquidity_finalized( self, pool_address: str, bpt_amount: int, max_data_token_amount: int, max_OCEAN_amount: int, from_wallet: Wallet, ) -> str: """ Add liquidity to a pool that's been finalized. Buy bpt_amount tokens from the pool, spending DataTokens and OCEAN tokens as needed and up to the specified maximum amounts. :param pool_address: str address of pool contract :param bpt_amount: int number of pool shares to receive for adding the liquidity :param max_data_token_amount: int maximum amount of Data tokens to go into the pool :param max_OCEAN_amount: int maximum amount of OCEAN tokens to go into the pool :param from_wallet: Wallet instance :return: str transaction id/hash """ assert self._is_valid_pool(pool_address), "The pool address is not valid." dt_address = self.get_token_address(pool_address) dt = BToken(self.web3, dt_address) if dt.balanceOf(from_wallet.address) < max_data_token_amount: raise InsufficientBalance( f"Insufficient funds for adding liquidity for {dt.address} datatoken!" ) if dt.allowance(from_wallet.address, pool_address) < max_data_token_amount: dt.approve(pool_address, max_data_token_amount, from_wallet=from_wallet) OCEAN = BToken(self.web3, self.ocean_address) if OCEAN.balanceOf(from_wallet.address) < max_OCEAN_amount: raise InsufficientBalance( f"Insufficient funds for adding liquidity for {OCEAN.address} OCEAN token!" ) if OCEAN.allowance(from_wallet.address, pool_address) < max_OCEAN_amount: OCEAN.approve(pool_address, max_OCEAN_amount, from_wallet=from_wallet) pool = BPool(self.web3, pool_address) return pool.joinPool( bpt_amount, [max_data_token_amount, max_OCEAN_amount], from_wallet=from_wallet, )