diff --git a/.bumpversion.cfg b/.bumpversion.cfg index cba62934a..697e8ab0a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.1.1 +current_version = 0.1.2 commit = True tag = True diff --git a/READMEs/marketplace_flow.md b/READMEs/marketplace_flow.md index a45335b33..90c1aca96 100644 --- a/READMEs/marketplace_flow.md +++ b/READMEs/marketplace_flow.md @@ -95,27 +95,36 @@ did = asset.did alice_wallet = Wallet() # From step 2 data_token = DataToken() # From step 2 -data_token.mint_tokens(alice_wallet.address, 100.0, alice_wallet) +data_token.mint_tokens(alice_wallet.address, 1000.0, alice_wallet) ``` -## 4. Alice allows marketplace to sell her datatokens +## 4. Alice creates a pool for trading her new data tokens ```python -data_token = DataToken() # From step 2 -marketplace_address = '0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0' -data_token.approve_tokens(marketplace_address, 20.0) +from ocean_lib.ocean.util import to_base_18 + +token_address = '' # From step 2 + +pool = ocean.pool.create( + token_address, + data_token_amount_base=to_base_18(500.0), + OCEAN_amount_base=to_base_18(5.0), + from_wallet=alice_wallet +) +pool_address = pool.address +print(f'DataToken @{data_token.address} has a `pool` available @{pool_address}') ``` -## 5. Marketplace posts asset for sale -Now, you're the marketplace:) +## 5. Marketplace posts asset for sale using price obtained from balancer pool ```python from ocean_utils.agreements.service_types import ServiceTypes from ocean_lib.ocean import Ocean from ocean_lib.ocean.util import from_base_18 +from ocean_lib.models.spool import SPool -#Market's config +# Market's config config = { 'network': 'rinkeby', } @@ -124,24 +133,44 @@ market_ocean = Ocean(config) did = '' # from step 2 asset = market_ocean.assets.resolve(did) service1 = asset.get_service(ServiceTypes.ASSET_ACCESS) -price = 10.0 # marketplace-set price of 10 USD / datatoken +pool_address = '' +pool = market_ocean.pool.get(pool_address) +# price in OCEAN tokens per data token +price_in_OCEAN = from_base_18(market_ocean.pool.get_token_price_base(pool_address)) # Display key asset information, such as the cost of each service -tokens_amount = from_base_18(service1.get_cost()) -print(f"Service 1 costs {tokens_amount * price} USD") # 1.5 * 10 = 15 +# Each access to an assets service requires ONE datatoken +tokens_amount = 1.0 +print(f"Service 1 costs {tokens_amount * price_in_OCEAN} OCEAN") +OCEAN_address = market_ocean.pool.ocean_address +OCEAN_usd_pool_address = '' +USDT_token_address = '' +ocn_pool = SPool(OCEAN_usd_pool_address) +OCEAN_price = from_base_18(ocn_pool.getSpotPrice( + tokenIn_address=USDT_token_address, + tokenOut_address=OCEAN_address +)) +print(f"Service 1 costs {tokens_amount * price_in_OCEAN * OCEAN_price} USD") ``` -## 6. Value swap: Bob buys datatokens from marketplace +## 6. Value swap: Bob buys datatokens from marketplace (using datatoken <> OCEAN balancer pool) ```python -# Not shown: in marketplace GUI, Bob uses Stripe to send USD to marketplace (or other methods / currencies). data_token = market_ocean.get_data_token(token_address) -data_token.transfer_tokens(dst_address=bob_address, 1.0) +# This assumes bob_wallet already has sufficient OCEAN tokens to buy the data token. OCEAN tokens +# can be obtained through a crypto exchange or an on-chain pool such as balancer or uniswap +market_ocean.pool.buy_data_tokens( + pool_address, + amount_base=to_base_18(1.0), # buy one data token + max_OCEAN_amount_base=to_base_18(0.1), # pay maximum 0.1 OCEAN tokens + from_wallet=bob_wallet +) + + ``` ## 7. Bob uses a service he just purchased (download) -Now, you're Bob:) ```python @@ -154,6 +183,13 @@ bob_ocean = Ocean(config) bob_wallet = Wallet(bob_ocean.web3, private_key='1234ef21b864d2cc526dbdb2a120bd2874c36c9d0a1fb7f8c63d7f7a8b41de8o') service = asset.get_service(ServiceTypes.ASSET_ACCESS) quote = bob_ocean.assets.order(asset.did, bob_wallet.address, service_index=service.index) -bob_ocean.assets.pay_for_service(quote.amount, quote.data_token_address, quote.receiver_address, bob_wallet) -file_path = bob_ocean.assets.download(asset.did, service.index, bob_wallet, '~/my-datasets') +transfer_tx_id = bob_ocean.assets.pay_for_service(quote.amount, quote.data_token_address, quote.receiver_address, bob_wallet) +file_path = bob_ocean.assets.download( + asset.did, + service.index, + bob_wallet, + transfer_tx_id, + destination='~/my-datasets', + index=0 +) ``` diff --git a/READMEs/simple_flow.md b/READMEs/simple_flow.md index 1548e374a..e99b32eb7 100644 --- a/READMEs/simple_flow.md +++ b/READMEs/simple_flow.md @@ -1,4 +1,4 @@ -from ocean_lib.ocean.util import get_dtfactory_addressfrom ocean_lib.ocean.util import get_dtfactory_address# Quickstart: Simple Flow +# Quickstart: Simple Flow This stripped-down flow shows the essence of Ocean. Just downloading, no metadata. @@ -80,8 +80,6 @@ token.transfer_tokens(bob_address, 1.0) ```python from ocean_lib.ocean import Ocean -from ocean_lib.models.dtfactory import DTFactory -from ocean_lib.ocean.util import get_dtfactory_address dt_address = '' # From first step bob_config = { diff --git a/docs/source/conf.py b/docs/source/conf.py index b924011dc..085a64daa 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -30,7 +30,7 @@ author = 'ocean-lib-py contributors' # The full version, including alpha/beta/rc tags -release = '0.1.1' +release = '0.1.2' # The short X.Y version release_parts = release.split('.') # a list version = release_parts[0] + '.' + release_parts[1] diff --git a/ocean_lib/__init__.py b/ocean_lib/__init__.py index 3cdfcc977..0826ad6e4 100644 --- a/ocean_lib/__init__.py +++ b/ocean_lib/__init__.py @@ -1,5 +1,5 @@ __author__ = """OceanProtocol""" -__version__ = '0.1.1' +__version__ = '0.1.2' # Copyright 2018 Ocean Protocol Foundation # SPDX-License-Identifier: Apache-2.0 diff --git a/ocean_lib/assets/asset_downloader.py b/ocean_lib/assets/asset_downloader.py index 9a836cce7..ea1b05572 100644 --- a/ocean_lib/assets/asset_downloader.py +++ b/ocean_lib/assets/asset_downloader.py @@ -18,7 +18,6 @@ def download_asset_files( token_address, token_transfer_tx_id, data_provider, - nonce, index=None ): """ @@ -31,7 +30,6 @@ def download_asset_files( :param token_address: hex str the address of the DataToken smart contract :param token_transfer_tx_id: hex str the token transfer transaction id (hash) :param data_provider: DataServiceProvider instance - :param nonce: int value to use in the signature :param index: Index of the document that is going to be downloaded, int :return: Asset folder path, str """ diff --git a/ocean_lib/data_provider/data_service_provider.py b/ocean_lib/data_provider/data_service_provider.py index 480c0116a..25ceb6c0f 100644 --- a/ocean_lib/data_provider/data_service_provider.py +++ b/ocean_lib/data_provider/data_service_provider.py @@ -72,6 +72,7 @@ def encrypt_files_dict(files_dict, encrypt_endpoint, asset_id, publisher_address def sign_message(wallet, msg, config, nonce=None): if nonce is None: nonce = DataServiceProvider.get_nonce(wallet.address, config) + print(f'signing message with nonce {nonce}: {msg}, account={wallet.address}') return Web3Helper.sign_hash( add_ethereum_prefix_and_hash_msg(f'{msg}{nonce}'), wallet diff --git a/ocean_lib/ocean/constants.py b/ocean_lib/ocean/constants.py index 293b90a4c..c01026b46 100644 --- a/ocean_lib/ocean/constants.py +++ b/ocean_lib/ocean/constants.py @@ -1,7 +1,7 @@ # configuration file CONF_FILE_PATH = '~/ocean.conf' -#Toggle runtime type-checking +# Toggle runtime type-checking import configparser, os config = configparser.ConfigParser() config.read(os.path.expanduser(CONF_FILE_PATH)) diff --git a/ocean_lib/ocean/ocean.py b/ocean_lib/ocean/ocean.py index 02986cfa6..06de6ce7d 100644 --- a/ocean_lib/ocean/ocean.py +++ b/ocean_lib/ocean/ocean.py @@ -5,7 +5,7 @@ import logging from ocean_lib.models.data_token import DataToken -from ocean_lib.ocean.ocean_market import OceanMarket +from ocean_lib.ocean.ocean_pool import OceanPool from ocean_lib.web3_internal.contract_handler import ContractHandler from ocean_lib.web3_internal.wallet import Wallet from ocean_lib.web3_internal.web3_provider import Web3Provider @@ -13,17 +13,13 @@ from ocean_lib.data_provider.data_service_provider import DataServiceProvider from ocean_lib.config_provider import ConfigProvider -from ocean_lib.models import balancer_constants -from ocean_lib.models.btoken import BToken from ocean_lib.models.dtfactory import DTFactory -from ocean_lib.models.sfactory import SFactory -from ocean_lib.models.spool import SPool from ocean_lib.ocean.ocean_assets import OceanAssets from ocean_lib.ocean.ocean_auth import OceanAuth from ocean_lib.ocean.ocean_compute import OceanCompute from ocean_lib.ocean.ocean_services import OceanServices -from ocean_lib.ocean.util import get_web3_connection_provider, get_dtfactory_address, get_sfactory_address, get_ocean_token_address +from ocean_lib.ocean.util import get_web3_connection_provider, get_ocean_token_address, get_sfactory_address from ocean_lib.web3_internal.web3helper import Web3Helper CONFIG_FILE_ENVIRONMENT_NAME = 'CONFIG_FILE' @@ -96,7 +92,9 @@ def __init__(self, config=None, data_provider=None): self._config, data_provider ) - self.ocean_market = OceanMarket(self._config, data_provider) + sfactory_address = get_sfactory_address(Web3Helper.get_network_name()) + ocean_address = get_ocean_token_address(Web3Helper.get_network_name()) + self.pool = OceanPool(ocean_address, sfactory_address) logger.debug('Ocean instance initialized: ') @@ -121,178 +119,7 @@ def get_data_token(self, token_address: str) -> DataToken: return DataToken(token_address) def get_dtfactory(self, dtfactory_address: str='') -> DTFactory: - dtf_address = dtfactory_address or get_dtfactory_address(Web3Helper.get_network_name()) + dtf_address = dtfactory_address or DTFactory.configured_address( + Web3Helper.get_network_name(), self._config.address_file) return DTFactory(dtf_address) - def create_pool(self, - DT_address: str, - num_DT_base: int, - num_OCEAN_base:int, - from_wallet: Wallet) -> SPool: - sfactory_address = get_sfactory_address(Web3Helper.get_network_name()) - - sfactory = SFactory(sfactory_address) - - pool_address = sfactory.newSPool(from_wallet) - pool = SPool(pool_address) - pool.setPublicSwap(True, from_wallet=from_wallet) - pool.setSwapFee(balancer_constants.DEFAULT_SWAP_FEE, from_wallet) - - DT = BToken(DT_address) - assert DT.balanceOf(from_wallet.address) >= num_DT_base, \ - "insufficient DT" - DT.approve(pool_address, num_DT_base, from_wallet=from_wallet) - pool.bind(DT_address, num_DT_base, balancer_constants.INIT_WEIGHT_DT, - from_wallet) - - OCEAN = BToken(self.OCEAN_address) - assert OCEAN.balanceOf(from_wallet.address) >= num_OCEAN_base, \ - "insufficient OCEAN" - OCEAN.approve(pool_address, num_OCEAN_base, from_wallet) - pool.bind(self.OCEAN_address, num_OCEAN_base, - balancer_constants.INIT_WEIGHT_OCEAN, from_wallet) - - return pool - - def get_pool(self, pool_address: str) -> SPool: - return SPool(pool_address) - - #============================================================ - #to simplify balancer flows. These methods are here because - # SPool doesn't know (and shouldn't know) OCEAN_address and _DT_address - def addLiquidity(self, pool_address: str, - num_DT_base: int, num_OCEAN_base: int, - from_wallet: Wallet): - DT_address = self._DT_address(pool_address) - self._addLiquidity(pool_address, DT_address, num_DT_base, - balancer_constants.INIT_WEIGHT_DT, from_wallet) - self._addLiquidity(pool_address, self.OCEAN_address, num_OCEAN_base, - balancer_constants.INIT_WEIGHT_OCEAN, from_wallet) - - def _addLiquidity(self, pool_address: str, token_address: str, - num_add_base: int, weight_base: int, from_wallet: Wallet): - assert num_add_base >= 0 - if num_add_base == 0: return - - token = BToken(token_address) - assert token.balanceOf(from_wallet.address) >= num_add_base, \ - "insufficient funds" - - token.approve(pool_address, num_add_base, from_wallet) - - pool = SPool(pool_address) - num_before_base = token.balanceOf(pool_address) - num_after_base = num_before_base + num_add_base - pool.rebind(token_address, num_after_base, weight_base, from_wallet) - - def remove_liquidity(self, pool_address: str, - num_DT_base:int, num_OCEAN_base:int, - from_wallet: Wallet): - DT_address = self._DT_address(pool_address) - self._remove_liquidity(pool_address, DT_address, num_DT_base, - balancer_constants.INIT_WEIGHT_DT, from_wallet) - self._remove_liquidity(pool_address, self.OCEAN_address, num_OCEAN_base, - balancer_constants.INIT_WEIGHT_OCEAN, from_wallet) - - def _remove_liquidity(self, pool_address: str, - token_address: str, num_remove_base: int, - weight_base: int, from_wallet: Wallet): - assert num_remove_base >= 0 - if num_remove_base == 0: return - - token = BToken(token_address) - num_before_base = token.balanceOf(pool_address) - num_after_base = num_before_base - num_remove_base - assert num_after_base >= 0, "tried to remove too much" - - pool = SPool(pool_address) - pool.rebind(token_address, num_after_base, weight_base, from_wallet) - - def buy_data_tokens(self, pool_address: str, - num_DT_base:int, max_num_OCEAN_base:int, - from_wallet: Wallet): - """ - Buy data tokens from this pool, if total spent <= max_num_OCEAN. - -Caller is spending OCEAN, and receiving DT - -OCEAN's going into pool, DT's going out of pool - """ - OCEAN = BToken(self.OCEAN_address) - OCEAN.approve(pool_address, max_num_OCEAN_base, from_wallet) - - DT_address = self._DT_address(pool_address) - pool = SPool(pool_address) - pool.swapExactAmountOut( - tokenIn_address = self.OCEAN_address, #entering pool - maxAmountIn_base = max_num_OCEAN_base, #"" - tokenOut_address = DT_address, #leaving pool - tokenAmountOut_base = num_DT_base, #"" - maxPrice_base = 2 ** 255, #here we limit by max_num_OCEAN, not price - from_wallet = from_wallet, - ) - - - def sell_data_tokens(self, pool_address: str, - num_DT_base: int, min_num_OCEAN_base: int, - from_wallet: Wallet): - """ - Sell data tokens into this pool, if total income >= min_num_OCEAN - -Caller is spending DT, and receiving OCEAN - -DT's going into pool, OCEAN's going out of pool - """ - DT_address = self._DT_address(pool_address) - DT = BToken(DT_address) - DT.approve(pool_address, num_DT_base, from_wallet=from_wallet) - - pool = SPool(pool_address) - pool.swapExactAmountIn( - tokenIn_address = DT_address, #entering pool - tokenAmountIn_base = num_DT_base, #"" - tokenOut_address = self.OCEAN_address, #leaving pool - minAmountOut_base = min_num_OCEAN_base, # "" - maxPrice_base = 2 ** 255, #here we limit by max_num_OCEAN, not price - from_wallet = from_wallet, - ) - - - def get_DT_price_base(self, pool_address: str) -> int: - DT_address = self._DT_address(pool_address) - pool = SPool(pool_address) - return pool.getSpotPrice( - tokenIn_address = self.OCEAN_address, - tokenOut_address = DT_address) - - - def add_liquidity_finalized( - self, pool_address: str, num_BPT_base: int, max_num_DT_base: int, - max_num_OCEAN_base: int, from_wallet: Wallet): - """Add liquidity to a pool that's been finalized. - Buy num_BPT tokens from the pool, spending DT and OCEAN as needed""" - assert self._is_valid_DT_OCEAN_pool(pool_address) - DT_address = self._DT_address(pool_address) - DT = BToken(DT_address) - DT.approve(pool_address, max_num_DT_base, from_wallet=from_wallet) - - OCEAN = BToken(self.OCEAN_address) - OCEAN.approve(pool_address, max_num_OCEAN_base, from_wallet=from_wallet) - - pool = SPool(pool_address) - pool.joinPool(num_BPT_base, [max_num_DT_base, max_num_OCEAN_base], - from_wallet=from_wallet) - - - def _DT_address(self, pool_address: str) -> str: - """Returns the address of this pool's datatoken.""" - assert self._is_valid_DT_OCEAN_pool(pool_address) - pool = SPool(pool_address) - return pool.getCurrentTokens()[0] - - - def _is_valid_DT_OCEAN_pool(self, pool_address) -> bool: - pool = SPool(pool_address) - if pool.getNumTokens() != 2: - return False - - #DT should be 0th token, OCEAN should be 1st token - if pool.getCurrentTokens()[1] != self.OCEAN_address: - return False - return True diff --git a/ocean_lib/ocean/ocean_assets.py b/ocean_lib/ocean/ocean_assets.py index 03ded6489..653d41e7d 100644 --- a/ocean_lib/ocean/ocean_assets.py +++ b/ocean_lib/ocean/ocean_assets.py @@ -10,6 +10,7 @@ from ocean_utils.agreements.service_agreement import ServiceAgreement from ocean_utils.agreements.service_factory import ServiceDescriptor, ServiceFactory from ocean_utils.agreements.service_types import ServiceTypes +from ocean_utils.aquarius.aquarius import Aquarius from ocean_utils.aquarius.aquarius_provider import AquariusProvider from ocean_utils.aquarius.exceptions import AquariusGenericError from ocean_utils.ddo.metadata import MetadataMain @@ -21,7 +22,7 @@ from ocean_utils.utils.utilities import checksum from ocean_lib.assets.asset import Asset -from ocean_lib.web3_internal.account import Account +from ocean_lib.data_provider.data_service_provider import OrderRequirements from ocean_lib.web3_internal.wallet import Wallet from ocean_lib.web3_internal.utils import add_ethereum_prefix_and_hash_msg from ocean_lib.assets.asset_downloader import download_asset_files @@ -29,7 +30,7 @@ from ocean_lib.models.data_token import DataToken from ocean_lib.models.dtfactory import DTFactory from ocean_lib.web3_internal.web3helper import Web3Helper -from ocean_lib.ocean.util import get_dtfactory_address, to_base_18 +from ocean_lib.ocean.util import to_base_18 logger = logging.getLogger('ocean') @@ -47,11 +48,10 @@ def __init__(self, config, data_provider): downloads_path = self._config.get('resources', 'downloads.path') or downloads_path self._downloads_path = downloads_path - def _get_aquarius(self, url=None): + def _get_aquarius(self, url=None) -> Aquarius: return AquariusProvider.get_aquarius(url or self._aquarius_url) - def _process_service_descriptors( - self, service_descriptors, metadata, account: Account): + def _process_service_descriptors(self, service_descriptors, metadata, wallet: Wallet) -> list: ddo_service_endpoint = self._get_aquarius().get_service_endpoint() service_type_to_descriptor = {sd[0]: sd for sd in service_descriptors} @@ -68,7 +68,7 @@ def _process_service_descriptors( access_service_descriptor = service_type_to_descriptor.pop( ServiceTypes.ASSET_ACCESS, ServiceDescriptor.access_service_descriptor( - self._build_access_service(metadata, to_base_18(1), account.address), + self._build_access_service(metadata, to_base_18(1), wallet.address), self._data_provider.get_download_endpoint(self._config) ) ) @@ -85,9 +85,9 @@ def _process_service_descriptors( _service_descriptors.extend(service_type_to_descriptor.values()) return ServiceFactory.build_services(_service_descriptors) - def create(self, metadata, publisher_wallet: Wallet, - service_descriptors=None, - owner_address=None, data_token_address=None): + def create(self, metadata: dict, publisher_wallet: Wallet, + service_descriptors: list=None, owner_address: str=None, + data_token_address: str=None) -> Asset: """ Register an asset on-chain by creating/deploying a DataToken contract and in the Metadata store (Aquarius). @@ -196,7 +196,8 @@ def create(self, metadata, publisher_wallet: Wallet, if not data_token_address: blob = json.dumps({'t': 1, 'url': ddo_service_endpoint}) # register on-chain - dtfactory = DTFactory(get_dtfactory_address(Web3Helper.get_network_name())) + address = DTFactory.configured_address(Web3Helper.get_network_name(), self._config.address_file) + dtfactory = DTFactory(address) tx_id = dtfactory.createToken(blob=blob, from_wallet=publisher_wallet) data_token = DataToken(dtfactory.get_token_address(tx_id)) if not data_token: @@ -244,7 +245,7 @@ def retire(self, did: str) -> bool: logger.error(err) return False - def resolve(self, did: str): + def resolve(self, did: str) -> Asset: """ When you pass a did retrieve the ddo associated. @@ -253,7 +254,7 @@ def resolve(self, did: str): """ return resolve_asset(did, metadata_store_url=self._config.aquarius_url) - def search(self, text: str, sort=None, offset=100, page=1, aquarius_url=None): + def search(self, text: str, sort=None, offset=100, page=1, aquarius_url=None) -> list: """ Search an asset in oceanDB using aquarius. @@ -270,7 +271,7 @@ def search(self, text: str, sort=None, offset=100, page=1, aquarius_url=None): return [Asset(dictionary=ddo_dict) for ddo_dict in self._get_aquarius(aquarius_url).text_search(text, sort, offset, page)['results']] - def query(self, query: dict, sort=None, offset=100, page=1, aquarius_url=None): + def query(self, query: dict, sort=None, offset=100, page=1, aquarius_url=None) -> []: """ Search an asset in oceanDB using search query. @@ -288,7 +289,8 @@ def query(self, query: dict, sort=None, offset=100, page=1, aquarius_url=None): return [Asset(dictionary=ddo_dict) for ddo_dict in aquarius.query_search(query, sort, offset, page)['results']] - def order(self, did, consumer_address, service_index=None, service_type=None): + def order(self, did: str, consumer_address: str, + service_index: [int, None]=None, service_type: str=None) -> OrderRequirements: """ Request a specific service from an asset, returns the service requirements that must be met prior to consuming the service. @@ -316,28 +318,29 @@ def order(self, did, consumer_address, service_index=None, service_type=None): return order_requirements @staticmethod - def pay_for_service(amount, token_address, receiver_address, from_account): + def pay_for_service(amount: int, token_address: str, + receiver_address: str, from_wallet: Wallet) -> str: """ Submits the payment for chosen service in DataTokens. :param amount: :param token_address: :param receiver_address: - :param from_account: + :param from_wallet: Wallet instance :return: hex str id of transfer transaction """ tokens_amount = int(amount) receiver = receiver_address dt = DataToken(token_address) - balance = dt.balanceOf(from_account.address) + balance = dt.balanceOf(from_wallet.address) if balance < tokens_amount: raise AssertionError(f'Your token balance {balance} is not sufficient ' f'to execute the requested service. This service ' f'requires {amount} number of tokens.') - tx_hash = dt.transfer(receiver, tokens_amount, from_account) + tx_hash = dt.transfer(receiver, tokens_amount, from_wallet) try: - return dt.verify_transfer_tx(tx_hash, from_account.address, receiver) + return dt.verify_transfer_tx(tx_hash, from_wallet.address, receiver) except (AssertionError, Exception) as e: msg = ( f'Downloading asset files failed. The problem is related to ' @@ -347,8 +350,8 @@ def pay_for_service(amount, token_address, receiver_address, from_account): logger.error(msg) raise AssertionError(msg) - def download(self, did, service_index, consumer_account, - transfer_tx_id, destination, nonce=None, index=None): + def download(self, did: str, service_index: int, consumer_wallet: Wallet, + transfer_tx_id: str, destination: str, index: [int, None]=None) -> str: """ Consume the asset data. @@ -361,7 +364,7 @@ def download(self, did, service_index, consumer_account, :param did: DID, str :param service_index: identifier of the service inside the asset DDO, str - :param consumer_account: Account instance of the consumer + :param consumer_wallet: Wallet instance of the consumer :param transfer_tx_id: hex str id of the token transfer transaction :param destination: str path :param nonce: int value to use in the signature @@ -373,9 +376,6 @@ def download(self, did, service_index, consumer_account, assert isinstance(index, int), logger.error('index has to be an integer.') assert index >= 0, logger.error('index has to be 0 or a positive integer.') - if nonce is None: - nonce = self._data_provider.get_nonce(consumer_account.address, self._config) - service = asset.get_service_by_index(service_index) assert service and service.type == ServiceTypes.ASSET_ACCESS, \ f'Service with index {service_index} and type {ServiceTypes.ASSET_ACCESS} is not found.' @@ -383,12 +383,11 @@ def download(self, did, service_index, consumer_account, return download_asset_files( service_index, asset, - consumer_account, + consumer_wallet, destination, asset.data_token_address, transfer_tx_id, self._data_provider, - nonce, index ) @@ -411,7 +410,7 @@ def owner(self, did: str) -> str: asset = self.resolve(did) return asset.publisher - def owner_assets(self, owner_address: str): + def owner_assets(self, owner_address: str) -> list: """ List of Asset objects published by ownerAddress @@ -421,7 +420,7 @@ def owner_assets(self, owner_address: str): return [asset.did for asset in self.query({"query": {"proof.creator": [owner_address]}}, offset=1000)] @staticmethod - def _build_access_service(metadata, cost: int, address: str) -> dict: + def _build_access_service(metadata: dict, cost: int, address: str) -> dict: return { "main": { "name": "dataAssetAccessServiceAgreement", diff --git a/ocean_lib/ocean/ocean_market.py b/ocean_lib/ocean/ocean_market.py deleted file mode 100644 index 9db36810e..000000000 --- a/ocean_lib/ocean/ocean_market.py +++ /dev/null @@ -1,11 +0,0 @@ - -class OceanMarket: - def __init__(self, config, data_provider): - self._config = config - self._data_provider = data_provider - - def buy_data_tokens(self, token_address, amount, price, currency): - return '' - - def get_data_token_price(self, token_address): - return 50, 'ocean' diff --git a/ocean_lib/ocean/ocean_pool.py b/ocean_lib/ocean/ocean_pool.py new file mode 100644 index 000000000..31323fb25 --- /dev/null +++ b/ocean_lib/ocean/ocean_pool.py @@ -0,0 +1,310 @@ +from ocean_lib.models import balancer_constants +from ocean_lib.models.btoken import BToken +from ocean_lib.models.sfactory import SFactory +from ocean_lib.models.spool import SPool +from ocean_lib.web3_internal.wallet import Wallet + + +class OceanPool: + """ + This pool is based on the Balancer protocol contracts with slight + modifications (https://github.com/balancer-labs). This class wraps the main + functionality needed to support publishing Data Tokens trading pools. + + A pool here always has OCEAN tokens on one end and some DataToken on the other end. + This allows the DataToken owner or any DataToken holder to create a pool for trading + the data token vs. OCEAN tokens. As a result all functions here assume the pool + has only two tokens and one of the tokens is always the OCEAN token. + + Note that the OCEAN token address is supplied to the init method. The Ocean instance + reads the OCEAN token address from the `address_file` config option (see Config.py). + + """ + + def __init__(self, ocean_token_address: str, sfactory_address: str): + self.ocean_address = ocean_token_address + self.sfactory_address = sfactory_address + + def create(self, + data_token_address: str, + data_token_amount_base: int, + OCEAN_amount_base: int, + from_wallet: Wallet) -> SPool: + """ + Create a new pool with bound datatoken and OCEAN token then finalize it. + The pool will have publicSwap enabled and swap fee is set + to `balancer_constants.DEFAULT_SWAP_FEE`. + Balances of both data tokens and OCEAN tokens must be sufficient in the + `from_wallet`, otherwise this will fail. + + :param data_token_address: str address of the DataToken contract + :param data_token_amount_base: int amount of initial liquidity of data tokens + :param OCEAN_amount_base: int amount of initial liquidity of OCEAN tokens + :param from_wallet: Wallet instance of pool owner + :return: SPool instance + """ + + sfactory = SFactory(self.sfactory_address) + + pool_address = sfactory.newSPool(from_wallet) + pool = SPool(pool_address) + pool.setPublicSwap(True, from_wallet=from_wallet) + pool.setSwapFee(balancer_constants.DEFAULT_SWAP_FEE, from_wallet) + + dt = BToken(data_token_address) + assert dt.balanceOf(from_wallet.address) >= data_token_amount_base, \ + f'insufficient DataTokens balance {dt.balanceOf(from_wallet.address)}' + dt.approve(pool_address, data_token_amount_base, from_wallet=from_wallet) + pool.bind( + data_token_address, + data_token_amount_base, + balancer_constants.INIT_WEIGHT_DT, + from_wallet + ) + + ocean_token = BToken(self.ocean_address) + assert ocean_token.balanceOf(from_wallet.address) >= OCEAN_amount_base, \ + f'insufficient OCEAN tokens balance {ocean_token.balanceOf(from_wallet.address)}' + ocean_token.approve(pool_address, OCEAN_amount_base, from_wallet) + pool.bind( + self.ocean_address, + OCEAN_amount_base, + balancer_constants.INIT_WEIGHT_OCEAN, + from_wallet + ) + + pool.finalize(from_wallet) + + return pool + + @staticmethod + def get(pool_address: str) -> SPool: + return SPool(pool_address) + + def get_token_address(self, pool_address: str) -> str: + """Returns the address of this pool's datatoken.""" + assert self._is_valid_pool(pool_address) + pool = SPool(pool_address) + tokens = pool.getCurrentTokens() + return tokens[0] if tokens[0] != self.ocean_address else tokens[1] + + def get_OCEAN_address(self) -> str: + return self.ocean_address + + # ============================================================ + # to simplify balancer flows. These methods are here because + # SPool doesn't know (and shouldn't know) OCEAN_address and _DT_address + def add_data_token_liquidity(self, pool_address: str, amount_base: int, from_wallet: Wallet) -> str: + """ + Add `amount_base` number of data tokens to the pool `pool_address`. In return the wallet owner + will get a number of pool shares/tokens + + The pool has a datatoken and OCEAN token. This function can be used to add liquidity of only + the datatoken. To add liquidity of the OCEAN token, use the `add_OCEAN_liquidity` function. + + :param pool_address: str address of pool contract + :param amount_base: number of data tokens to add to this pool + :param from_wallet: Wallet instance of the owner of data tokens + :return: str transaction id/hash + """ + return self._add_liquidity( + pool_address, self.get_token_address(pool_address), amount_base, from_wallet + ) + + def add_OCEAN_liquidity(self, pool_address: str, amount_base: int, from_wallet: Wallet) -> str: + """ + Add `amount_base` number of OCEAN tokens to the pool `pool_address`. In return the wallet owner + will get a number of pool shares/tokens + + :param pool_address: str address of pool contract + :param amount_base: number of data tokens to add to this pool + :param from_wallet: Wallet instance of the owner of data tokens + :return: str transaction id/hash + """ + return self._add_liquidity( + pool_address, self.ocean_address, amount_base, 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 = SPool(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 remove_data_token_liquidity(self, pool_address: str, amount_base: int, + max_pool_shares_base: int, from_wallet: Wallet) -> str: + """ + Remove `amount_base` number of data tokens from the pool `pool_address`. The wallet owner + will get that amount of data tokens. At the same time a number of pool shares/tokens up to + `max_pool_shares_base` will be taken from the caller's wallet and given back to the pool. + + :param pool_address: str address of pool contract + :param amount_base: int number of data tokens to add to this pool in *base* + :param max_pool_shares_base: int maximum number of pool shares as a cost for the withdrawn data tokens + :param from_wallet: Wallet instance of the owner of data tokens + :return: str transaction id/hash + """ + dt_address = self.get_token_address(pool_address) + return self._remove_liquidity( + pool_address, dt_address, amount_base, max_pool_shares_base, from_wallet + ) + + def remove_OCEAN_liquidity(self, pool_address: str, amount_base: int, + max_pool_shares_base: int, from_wallet: Wallet) -> str: + """ + Remove `amount_base` number of OCEAN tokens from the pool `pool_address`. The wallet owner + will get that amount of OCEAN tokens. At the same time a number of pool shares/tokens up to + `max_pool_shares_base` will be taken from the caller's wallet and given back to the pool. + + :param pool_address: str address of pool contract + :param amount_base: int number of data tokens to add to this pool in *base* + :param max_pool_shares_base: int maximum number of pool shares as a cost for the withdrawn data tokens + :param from_wallet: Wallet instance of the owner of data tokens + :return: str transaction id/hash + """ + return self._remove_liquidity( + pool_address, self.ocean_address, amount_base, max_pool_shares_base, from_wallet + ) + + def _remove_liquidity(self, pool_address: str, token_address: str, amount_base: int, + max_pool_shares_base: int, from_wallet: Wallet) -> str: + assert amount_base >= 0 + if amount_base == 0: + return '' + + assert max_pool_shares_base > 0, f'' + + pool = SPool(pool_address) + if pool.balanceOf(from_wallet.address) == 0: + return '' + + return pool.exitswapExternAmountOut(token_address, amount_base, max_pool_shares_base, from_wallet) + + def buy_data_tokens(self, pool_address: str, amount_base: int, + max_OCEAN_amount_base: int, from_wallet: Wallet) -> str: + """ + Buy data tokens from this pool, paying `max_OCEAN_amount_base` of OCEAN tokens. + If total spent <= max_OCEAN_amount_base. + - Caller is spending OCEAN tokens, and receiving `amount_base` DataTokens + - OCEAN tokens are going into pool, DataTokens are going out of pool + + The transaction fails if total spent exceeds `max_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 max_OCEAN_amount_base: + :param from_wallet: + :return: str transaction id/hash + """ + ocean_tok = BToken(self.ocean_address) + ocean_tok.approve(pool_address, max_OCEAN_amount_base, from_wallet) + + dtoken_address = self.get_token_address(pool_address) + pool = SPool(pool_address) + return pool.swapExactAmountOut( + tokenIn_address=self.ocean_address, # entering pool + maxAmountIn_base=max_OCEAN_amount_base, # "" + tokenOut_address=dtoken_address, # leaving pool + tokenAmountOut_base=amount_base, # "" + maxPrice_base=2 ** 255, # here we limit by max_num_OCEAN, not price + from_wallet=from_wallet, + ) + + 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 = SPool(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 get_token_price_base(self, pool_address: str) -> int: + """ + + :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 = SPool(pool_address) + return pool.getSpotPrice( + tokenIn_address=self.ocean_address, + tokenOut_address=dtoken_address + ) + + 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 = SPool(pool_address) + return pool.joinPool( + bpt_amount_base, + [max_data_token_amount_base, max_OCEAN_amount_base], + from_wallet=from_wallet + ) + + def _is_valid_pool(self, pool_address) -> bool: + pool = SPool(pool_address) + if pool.getNumTokens() != 2: + return False + + # dt should be 0th token, OCEAN should be 1st token + if pool.getCurrentTokens()[1] != self.ocean_address: + return False + return True diff --git a/ocean_lib/ocean/util.py b/ocean_lib/ocean/util.py index 9081d263c..0716dad74 100644 --- a/ocean_lib/ocean/util.py +++ b/ocean_lib/ocean/util.py @@ -5,6 +5,7 @@ from ocean_lib.config_provider import ConfigProvider from ocean_lib.models.dtfactory import DTFactory from ocean_lib.models.sfactory import SFactory +from ocean_lib.web3_internal.contract_handler import ContractHandler from ocean_lib.web3_internal.web3_overrides.http_provider import CustomHTTPProvider from ocean_lib.web3_internal.web3helper import Web3Helper @@ -63,13 +64,7 @@ def get_web3_connection_provider(network_url): def get_contracts_addresses(network, config): - _file = config.address_file - if not _file or not os.path.exists(_file): - return None - with open(_file) as f: - addresses = json.load(f) - - return addresses.get(network, None) + return ContractHandler.get_contracts_addresses(network, config.address_file) def to_base_18(amt: float) -> int: @@ -91,18 +86,18 @@ def from_base(num_base: int, dec: int) -> float: def get_dtfactory_address(network=None): - if network is None: - network = Web3Helper.get_network_name() - return get_contracts_addresses(network, ConfigProvider.get_config()).get(DTFactory.CONTRACT_NAME) + return DTFactory.configured_address( + network or Web3Helper.get_network_name(), ConfigProvider.get_config().address_file + ) def get_sfactory_address(network=None): - if network is None: - network = Web3Helper.get_network_name() - return get_contracts_addresses(network, ConfigProvider.get_config()).get(SFactory.CONTRACT_NAME) + return SFactory.configured_address( + network or Web3Helper.get_network_name(), ConfigProvider.get_config().address_file + ) def get_ocean_token_address(network=None): - if network is None: - network = Web3Helper.get_network_name() - return get_contracts_addresses(network, ConfigProvider.get_config()).get('Ocean') + return get_contracts_addresses( + network or Web3Helper.get_network_name(), ConfigProvider.get_config() + ).get('Ocean') diff --git a/ocean_lib/web3_internal/contract_base.py b/ocean_lib/web3_internal/contract_base.py index 1ccbf7aec..e5fa47b91 100644 --- a/ocean_lib/web3_internal/contract_base.py +++ b/ocean_lib/web3_internal/contract_base.py @@ -38,6 +38,11 @@ def __init__(self, address: [str, None], abi_path=None): def __str__(self): return f'{self.contract_name} @ {self.address}' + @classmethod + def configured_address(cls, network, address_file): + return ContractHandler.get_contracts_addresses( + network, address_file).get(cls.CONTRACT_NAME) + @property def contract_name(self) -> str: return self.CONTRACT_NAME diff --git a/ocean_lib/web3_internal/contract_handler.py b/ocean_lib/web3_internal/contract_handler.py index f7b6f1878..26d5b4a12 100644 --- a/ocean_lib/web3_internal/contract_handler.py +++ b/ocean_lib/web3_internal/contract_handler.py @@ -25,6 +25,15 @@ class ContractHandler(object): _contracts = dict() artifacts_path = None + @staticmethod + def get_contracts_addresses(network, address_file): + if not address_file or not os.path.exists(address_file): + return None + with open(address_file) as f: + addresses = json.load(f) + + return addresses.get(network, None) + @staticmethod def set_artifacts_path(artifacts_path): if artifacts_path != ContractHandler.artifacts_path: diff --git a/setup.py b/setup.py index a9e8f536d..ae3c4a32a 100644 --- a/setup.py +++ b/setup.py @@ -96,6 +96,6 @@ test_suite='tests', tests_require=test_requirements, url='https://github.com/oceanprotocol/ocean-lib-py', - version='0.1.1', + version='0.1.2', zip_safe=False, ) diff --git a/tests/conftest.py b/tests/conftest.py index 503e824d2..d65492908 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,7 @@ from ocean_lib.web3_internal.web3_provider import Web3Provider from examples import ExampleConfig -from ocean_lib.ocean.util import get_web3_connection_provider +from ocean_lib.ocean.util import get_web3_connection_provider, to_base_18, get_ocean_token_address from tests.resources.helper_functions import ( get_metadata, setup_logging, @@ -37,12 +37,19 @@ def setup_all(): print(f'sender: {wallet.key}, {wallet.address}, {wallet.password}, {wallet.keysStr()}') print(f'sender balance: {Web3Helper.from_wei(Web3Helper.get_ether_balance(wallet.address))}') - assert Web3Helper.from_wei(Web3Helper.get_ether_balance(wallet.address)) > 50 + assert Web3Helper.from_wei(Web3Helper.get_ether_balance(wallet.address)) > 10 + from ocean_lib.models.data_token import DataToken + OCEAN_token = DataToken(get_ocean_token_address(network)) + amt_distribute = 1000 + amt_distribute_base = to_base_18(float(amt_distribute)) for w in (get_publisher_wallet(), get_consumer_wallet()): if Web3Helper.from_wei(Web3Helper.get_ether_balance(w.address)) < 2: Web3Helper.send_ether(wallet, w.address, 4) + if OCEAN_token.token_balance(w.address) < 100: + OCEAN_token.transfer(w.address, amt_distribute_base, from_wallet=wallet) + @pytest.fixture def publisher_ocean_instance(): diff --git a/tests/models/conftest.py b/tests/models/conftest.py index 58e2baeb1..0f8108c99 100644 --- a/tests/models/conftest.py +++ b/tests/models/conftest.py @@ -2,9 +2,11 @@ import pytest +from ocean_lib.config_provider import ConfigProvider from ocean_lib.models.data_token import DataToken from ocean_lib.models.dtfactory import DTFactory -from ocean_lib.ocean.util import get_dtfactory_address, get_sfactory_address, get_ocean_token_address, to_base_18 +from ocean_lib.models.sfactory import SFactory +from ocean_lib.ocean.util import get_ocean_token_address, to_base_18 from ocean_lib.web3_internal.account import Account from ocean_lib.web3_internal.wallet import Wallet from ocean_lib.models import btoken @@ -24,12 +26,12 @@ def network(): @pytest.fixture def dtfactory_address(): - return get_dtfactory_address(_NETWORK) + return DTFactory.configured_address(_NETWORK, ConfigProvider.get_config().address_file) @pytest.fixture def sfactory_address(): - return get_sfactory_address(_NETWORK) + return SFactory.configured_address(_NETWORK, ConfigProvider.get_config().address_file) @pytest.fixture diff --git a/tests/models/test_balancer_simple_for_users_flow.py b/tests/models/test_balancer_simple_for_users_flow.py index 1b3b13c4f..ce1315aba 100644 --- a/tests/models/test_balancer_simple_for_users_flow.py +++ b/tests/models/test_balancer_simple_for_users_flow.py @@ -2,106 +2,121 @@ from ocean_lib.ocean.util import to_base_18, from_base_18 + def test_quickstart(alice_ocean, alice_wallet, alice_address, - bob_ocean, bob_wallet): - #=============================================================== + bob_ocean, bob_wallet): + # =============================================================== # 1. Alice publishes a dataset (= publishes a datatoken) # For now, you're Alice:) Let's proceed. DT = alice_ocean.create_data_token('localhost:8030', alice_wallet) 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) - pool = alice_ocean.create_pool( - DT_address, num_DT_base=to_base_18(90.0), num_OCEAN_base=to_base_18(10.0), + pool = alice_ocean.pool.create( + DT_address, data_token_amount_base=to_base_18(90.0), OCEAN_amount_base=to_base_18(10.0), from_wallet=alice_wallet) pool_address = pool.address - - #=============================================================== + + assert pool.isFinalized(), f'create pool should finalize the pool.' + assert pool.isPublicSwap(), f'create pool should have publicSwap enabled.' + + # =============================================================== # 5. Alice adds liquidity to pool - alice_ocean.addLiquidity( - pool_address, num_DT_base=to_base_18(9.0), num_OCEAN_base=to_base_18(1.0), - from_wallet=alice_wallet) - - #=============================================================== + alice_ocean.pool.add_data_token_liquidity( + pool_address, amount_base=to_base_18(9.0), from_wallet=alice_wallet) + dt_pool_shares = pool.balanceOf(alice_wallet.address) + + alice_ocean.pool.add_OCEAN_liquidity( + pool_address, amount_base=to_base_18(1.0), from_wallet=alice_wallet) + ocn_pool_shares = pool.balanceOf(alice_wallet.address) + + # =============================================================== # 6. Bob buys a DT from pool - alice_ocean.buy_data_tokens(pool_address, - num_DT_base=to_base_18(1.0), - max_num_OCEAN_base=to_base_18(2.0), - from_wallet=bob_wallet) - - #=============================================================== + alice_ocean.pool.buy_data_tokens(pool_address, + amount_base=to_base_18(1.0), + max_OCEAN_amount_base=to_base_18(2.0), + from_wallet=bob_wallet) + + # =============================================================== # 7. Bob consumes dataset # - - #=============================================================== + + # =============================================================== # 8. Alice removes liquidity - alice_ocean.remove_liquidity(pool_address, - num_DT_base=to_base_18(2.0), - num_OCEAN_base=to_base_18(3.0), - from_wallet=alice_wallet) - - #=============================================================== + alice_ocean.pool.remove_data_token_liquidity(pool_address, + amount_base=to_base_18(2.0), + max_pool_shares_base=dt_pool_shares, + from_wallet=alice_wallet) + + alice_ocean.pool.remove_OCEAN_liquidity(pool_address, + amount_base=to_base_18(3.0), + max_pool_shares_base=ocn_pool_shares, + from_wallet=alice_wallet) + + # =============================================================== # 9. Alice sells data tokens - alice_ocean.sell_data_tokens(pool_address, - num_DT_base=to_base_18(1.0), - min_num_OCEAN_base=to_base_18(0.0001), - from_wallet=alice_wallet) - - #=============================================================== - # 10. Alice finalizes pool. Now others can add liquidity. - pool.finalize(from_wallet=alice_wallet) - - #=============================================================== + alice_ocean.pool.sell_data_tokens(pool_address, + amount_base=to_base_18(1.0), + min_OCEAN_amount_base=to_base_18(0.0001), + from_wallet=alice_wallet) + + # =============================================================== # 11. Bob adds liquidity - bob_ocean.add_liquidity_finalized( + bob_ocean.pool.add_data_token_liquidity( + pool_address, + amount_base=to_base_18(0.1), + from_wallet=bob_wallet) + bob_ocean.pool.add_OCEAN_liquidity( pool_address, - num_BPT_base=to_base_18(0.1), max_num_DT_base=to_base_18(1.0), - max_num_OCEAN_base=to_base_18(1.0), + amount_base=to_base_18(1.0), from_wallet=bob_wallet) - - #=============================================================== + + # =============================================================== # 12. Bob adds liquidity AGAIN - bob_ocean.add_liquidity_finalized( + bob_ocean.pool.add_data_token_liquidity( pool_address, - num_BPT_base=to_base_18(0.1), max_num_DT_base=to_base_18(1.0), - max_num_OCEAN_base=to_base_18(1.0), + amount_base=to_base_18(0.2), from_wallet=bob_wallet) + bob_ocean.pool.add_OCEAN_liquidity( + pool_address, + amount_base=to_base_18(0.1), + from_wallet=bob_wallet) + -#=============================================================== +# =============================================================== # Test helper functions for the quickstart stuff above def test_ocean_balancer_helpers( OCEAN_address, alice_ocean, alice_wallet, alice_address, bob_ocean): - DT = alice_ocean.create_data_token('foo', alice_wallet) DT.mint(alice_address, to_base_18(1000.0), alice_wallet) - with pytest.raises(Exception): #not enough liquidity - pool = alice_ocean.create_pool( - DT.address, num_DT_base=0, num_OCEAN_base=0, + with pytest.raises(Exception): # not enough liquidity + pool = alice_ocean.pool.create( + DT.address, data_token_amount_base=0, OCEAN_amount_base=0, from_wallet=alice_wallet) - - pool = alice_ocean.create_pool( - DT.address, num_DT_base=to_base_18(90.0), num_OCEAN_base=to_base_18(10.0), + + pool = alice_ocean.pool.create( + DT.address, data_token_amount_base=to_base_18(90.0), OCEAN_amount_base=to_base_18(10.0), from_wallet=alice_wallet) pool_address = pool.address - assert alice_ocean.OCEAN_address == OCEAN_address - assert alice_ocean._DT_address(pool.address) == DT.address + assert alice_ocean.pool.ocean_address == OCEAN_address + assert alice_ocean.pool.get_token_address(pool.address) == DT.address + + assert alice_ocean.pool.get(pool_address).address == pool_address + assert bob_ocean.pool.get(pool_address).address == pool_address - assert alice_ocean.get_pool(pool_address).address == pool_address - assert bob_ocean.get_pool(pool_address).address == pool_address - - DT_price = from_base_18(bob_ocean.get_DT_price_base(pool_address)) + DT_price = from_base_18(bob_ocean.pool.get_token_price_base(pool_address)) assert DT_price > 0.0 diff --git a/tests/ocean/test_market_flow.py b/tests/ocean/test_market_flow.py index d898def60..f4ce18a2a 100644 --- a/tests/ocean/test_market_flow.py +++ b/tests/ocean/test_market_flow.py @@ -65,8 +65,7 @@ def test_market_flow(): sa.index, consumer_wallet, payment_tx_id, - consumer_ocean.config.downloads_path, - nonce=order_requirements.nonce + consumer_ocean.config.downloads_path ) assert len(os.listdir(asset_folder)) > 1 diff --git a/tests/ocean/test_simple_flow.py b/tests/ocean/test_simple_flow.py index 2edb36a85..ba9a153fd 100644 --- a/tests/ocean/test_simple_flow.py +++ b/tests/ocean/test_simple_flow.py @@ -4,7 +4,6 @@ from ocean_lib.ocean import Ocean from ocean_lib.models.dtfactory import DTFactory -from ocean_lib.ocean.util import get_dtfactory_address from ocean_lib.web3_internal.web3helper import Web3Helper from tests.resources.helper_functions import get_publisher_wallet, get_consumer_wallet @@ -18,7 +17,7 @@ def test_simple_flow(): # 1. Alice publishes a dataset (= publishes a datatoken) config = { 'network': _config.network_url, - 'factory.address': get_dtfactory_address(network), + 'factory.address': DTFactory.configured_address(network, _config.address_file), } ocean = Ocean(config) token = ocean.create_data_token(dataset_download_endpoint, alice_wallet) @@ -34,7 +33,8 @@ def test_simple_flow(): # 5. Bob consumes dataset bob_ocean = Ocean(config) token = bob_ocean.get_data_token(dt_address) - token_owner = DTFactory(get_dtfactory_address(network)).get_token_minter(token.address) + address = DTFactory.configured_address(network, _config.address_file) + token_owner = DTFactory(address).get_token_minter(token.address) tx_id = token.transfer_tokens(token_owner, 1, bob_wallet)