diff --git a/.dockerignore b/.dockerignore index 59a09431..2fc1cbae 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,3 @@ .github/ .python-version +/node_modules/ diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6f954521..8da10e9f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -14,11 +14,11 @@ jobs: steps: - uses: actions/checkout@v4 - name: run unit test using postgres - run: docker-compose run ibet-prime-postgres + run: docker compose run ibet-prime-postgres migration-test-postgres: name: 'Migration tests (PostgreSQL)' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: run unit test using postgres - run: docker-compose run ibet-prime-postgres bash --login -c "cd /app/ibet-Prime && pytest -vv --test-alembic -m 'alembic'" + run: docker compose run ibet-prime-postgres bash --login -c "cd /app/ibet-Prime && pytest -vv --test-alembic -m 'alembic'" diff --git a/.gitignore b/.gitignore index ee20fe18..254f4880 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ __pycache__/ # pyenv .python-version +# Node +node_modules/ + # Intellij .idea /*.iml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b01cb4fa..08fe14c4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 24.1.1 + rev: 24.4.2 hooks: - id: black - language_version: python3.11 + language_version: python3.12 diff --git a/Dockerfile b/Dockerfile index b9fb483e..11cc55d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,15 +52,16 @@ RUN echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~apl/.bash_profile \ # install python USER apl RUN . ~/.bash_profile \ - && pyenv install 3.11.2 \ - && pyenv global 3.11.2 \ + && pyenv install 3.12.2 \ + && pyenv global 3.12.2 \ && pip install --upgrade pip setuptools # install poetry RUN . ~/.bash_profile \ - && python -m pip install poetry==1.7.1 + && python -m pip install poetry==1.8.2 RUN . ~/.bash_profile \ - && poetry config virtualenvs.create false + && poetry config virtualenvs.create false \ + && poetry config installer.max-workers 1 # install python packages USER root @@ -88,10 +89,10 @@ COPY pyproject.toml /app/ibet-Prime/pyproject.toml COPY poetry.lock /app/ibet-Prime/poetry.lock RUN . ~/.bash_profile \ && cd /app/ibet-Prime \ - && poetry install --only main --no-root -E ibet-explorer \ + && poetry install --only main --no-root --all-extras \ && rm -f /app/ibet-Prime/pyproject.toml \ && rm -f /app/ibet-Prime/poetry.lock -ENV PYTHONPATH /app/ibet-Prime +ENV PYTHONPATH /app/ibet-Prime:/app/ibet-Prime/cmd # command deploy USER apl diff --git a/Makefile b/Makefile index 1bff99f8..5e623c90 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ -.PHONY: isort black test run +.PHONY: format isort black doc test test_migrations run install: - poetry install --no-root -E ibet-explorer + poetry install --no-root --all-extras poetry run pre-commit install update: @@ -15,6 +15,9 @@ isort: black: black . +doc: + poetry run python docs/generate_openapi_doc.py + test: pytest tests/ diff --git a/README.md b/README.md index 662d0fcf..ea624c1e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # ibet-Prime

- Version + Version License: Apache--2.0

@@ -22,11 +22,11 @@ English | [日本語](./README_JA.md) ## Dependencies -- [Python3](https://www.python.org/downloads/release/python-3811/) - version 3.11 +- [Python3](https://www.python.org/downloads/release/python-3811/) - version 3.12 - [PostgreSQL](https://www.postgresql.org/) - version 15 - [GoQuorum](https://github.com/ConsenSys/quorum) - We support the official GoQuorum node of [ibet-Network](https://github.com/BoostryJP/ibet-Network). - - We use [ganache](https://github.com/trufflesuite/ganache) for local development and unit testing, and we use the latest version. + - We use [hardhat network](https://hardhat.org/hardhat-network/) for local development and unit testing, and we use the latest version. ## Supported ibet smart contract version @@ -52,7 +52,7 @@ English | [日本語](./README_JA.md) Install python packages with: ```bash -$ poetry install --no-root --only main -E ibet-explorer +$ poetry install --no-root --only main --all-extras ``` ### Install pre-commit hook @@ -60,6 +60,11 @@ $ poetry install --no-root --only main -E ibet-explorer $ poetry run pre-commit install ``` +### Install hardhat +```bash +$ npm install +``` + ### Setting environment variables The main environment variables are as follows. diff --git a/README_JA.md b/README_JA.md index 8a692a1b..8cdb2d86 100644 --- a/README_JA.md +++ b/README_JA.md @@ -5,7 +5,7 @@ # ibet-Prime

- Version + Version License: Apache--2.0

@@ -23,11 +23,11 @@ ## 依存 -- [Python3](https://www.python.org/downloads/release/python-3811/) - バージョン 3.11 +- [Python3](https://www.python.org/downloads/release/python-3811/) - バージョン 3.12 - [PostgreSQL](https://www.postgresql.org/) - バージョン 15 - [GoQuorum](https://github.com/ConsenSys/quorum) - [ibet-Network](https://github.com/BoostryJP/ibet-Network) の公式の GoQuorum をサポートしています。 - - 最新の [ganache](https://github.com/trufflesuite/ganache) (ganache-cli) をローカル開発およびユニットテストで利用しています。 + - 最新の [hardhat network](https://hardhat.org/hardhat-network/) をローカル開発およびユニットテストで利用しています。 ## コントラクトのバージョン @@ -53,7 +53,7 @@ 以下のコマンドで Python パッケージをインストールします。 ```bash -$ poetry install --no-root --only main -E ibet-explorer +$ poetry install --no-root --only main --all-extras ``` ### pre-commit hookのインストール @@ -61,6 +61,11 @@ $ poetry install --no-root --only main -E ibet-explorer $ poetry run pre-commit install ``` +### hardhatのインストール +```bash +$ npm install +``` + ### 環境変数の設定 主要な環境変数は以下の通りです。 diff --git a/app/exceptions.py b/app/exceptions.py index 158ed82c..9db334fb 100644 --- a/app/exceptions.py +++ b/app/exceptions.py @@ -56,6 +56,15 @@ class Integer64bitLimitExceededError(AppError): code = 5 +class OperationNotSupportedVersionError(AppError): + """ + The token version for which the operation is not supported + """ + + status_code = status.HTTP_400_BAD_REQUEST + code = 6 + + class OperationNotAllowedStateError(AppError): """ Error returned when server-side data is not ready to process the request diff --git a/app/log.py b/app/log.py index fffd1528..b5b66281 100644 --- a/app/log.py +++ b/app/log.py @@ -20,7 +20,7 @@ import logging import sys import urllib -from datetime import datetime +from datetime import UTC, datetime from fastapi import Request, Response @@ -34,6 +34,9 @@ ACCESS_LOG = logging.getLogger("issuer_api_access") ACCESS_LOG.propagate = False +logging.getLogger("web3.manager.RequestManager").propagate = False +logging.getLogger("web3.manager.RequestManager").addHandler(logging.NullHandler()) + INFO_FORMAT = "[%(asctime)s] {}[%(process)d] [%(levelname)s] %(message)s" DEBUG_FORMAT = "[%(asctime)s] {}[%(process)d] [%(levelname)s] %(message)s [in %(pathname)s:%(lineno)d]" TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S %z" @@ -108,7 +111,9 @@ def output_access_log(req: Request, res: Response, request_start_time: datetime) method = req.scope.get("method", "") http_version = req.scope.get("http_version", "") status_code = res.status_code - response_time = (datetime.utcnow() - request_start_time).total_seconds() + response_time = ( + datetime.now(UTC).replace(tzinfo=None) - request_start_time + ).total_seconds() access_msg = ACCESS_FORMAT % ( method, url, diff --git a/app/main.py b/app/main.py index 0a64e74f..55167cfd 100644 --- a/app/main.py +++ b/app/main.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime +from datetime import UTC, datetime from fastapi import FastAPI, Request from fastapi.encoders import jsonable_encoder @@ -40,6 +40,7 @@ ledger, notification, position, + settlement, share, token_holders, ) @@ -62,7 +63,7 @@ app = FastAPI( title="ibet Prime", description="Security token management system for ibet network", - version="24.3.0", + version="24.6", contact={"email": "dev@boostry.co.jp"}, license_info={ "name": "Apache 2.0", @@ -74,7 +75,7 @@ @app.middleware("http") async def api_call_handler(request: Request, call_next): - request_start_time = datetime.utcnow() + request_start_time = datetime.now(UTC).replace(tzinfo=None) response = await call_next(request) output_access_log(request, response, request_start_time) return response @@ -105,6 +106,7 @@ async def root(): app.include_router(token_holders.router) app.include_router(bc_explorer.router) app.include_router(freeze_log.router) +app.include_router(settlement.router) ############################################################### @@ -206,7 +208,7 @@ async def response_limit_exceeded_error_handler( ) -# 400:ResponseLimitExceededError +# 400:Integer64bitLimitExceededError @app.exception_handler(Integer64bitLimitExceededError) async def response_limit_exceeded_error_handler( request: Request, exc: Integer64bitLimitExceededError @@ -218,6 +220,18 @@ async def response_limit_exceeded_error_handler( ) +# 400:OperationNotSupportedVersionError +@app.exception_handler(OperationNotSupportedVersionError) +async def operation_not_supported_version_error_handler( + request: Request, exc: OperationNotSupportedVersionError +): + meta = {"code": 6, "title": "OperationNotSupportedVersionError"} + return JSONResponse( + status_code=exc.status_code, + content=jsonable_encoder({"meta": meta, "detail": exc.args[0]}), + ) + + # 400:OperationNotAllowedStateError @app.exception_handler(OperationNotAllowedStateError) async def operation_not_permitted_error_handler( diff --git a/app/model/__init__.py b/app/model/__init__.py index f8bf1b6e..3684b4dd 100644 --- a/app/model/__init__.py +++ b/app/model/__init__.py @@ -17,6 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ +from datetime import datetime from typing import Annotated, Any from pydantic import WrapValidator @@ -37,3 +38,28 @@ def ethereum_address_validator( EthereumAddress = Annotated[str, WrapValidator(ethereum_address_validator)] + + +def datetime_string_validator( + value: Any, handler: ValidatorFunctionWrapHandler, *args, **kwargs +): + """Validate string datetime format + + - %Y/%m/%d %H:%M:%S + """ + if value is not None: + datetime_format = "%Y-%m-%d %H:%M:%S" + + if not isinstance(value, str): + raise ValueError(f"value must be of string datetime format") + + try: + converted = datetime.strptime(value, datetime_format) + if value != converted.strftime(datetime_format): + raise ValueError(f"value must be string datetime format") + except ValueError: + raise ValueError(f"value must be of string datetime format") + return value + + +ValidatedDatetimeStr = Annotated[str, WrapValidator(datetime_string_validator)] diff --git a/app/model/blockchain/__init__.py b/app/model/blockchain/__init__.py index 118c01e2..4dc0dd3f 100644 --- a/app/model/blockchain/__init__.py +++ b/app/model/blockchain/__init__.py @@ -20,7 +20,7 @@ from .e2e_messaging import E2EMessaging from .exchange import IbetExchangeInterface, IbetSecurityTokenEscrow from .freeze_log import FreezeLogContract -from .personal_info import PersonalInfoContract +from .personal_info import ContractPersonalInfoType, PersonalInfoContract from .token import ( IbetSecurityTokenInterface, IbetShareContract, diff --git a/app/model/blockchain/exchange.py b/app/model/blockchain/exchange.py index 385c2778..981d41b9 100644 --- a/app/model/blockchain/exchange.py +++ b/app/model/blockchain/exchange.py @@ -20,6 +20,12 @@ from web3.exceptions import TimeExhausted from app.exceptions import ContractRevertError, SendTransactionError +from app.model.blockchain.tx_params.ibet_security_token_dvp import ( + AbortDeliveryParams, + CancelDeliveryParams, + CreateDeliveryParams, + FinishDeliveryParams, +) from app.model.blockchain.tx_params.ibet_security_token_escrow import ( ApproveTransferParams, ) @@ -99,3 +105,120 @@ async def approve_transfer( raise SendTransactionError(timeout_error) except Exception as err: raise SendTransactionError(err) + + +class IbetSecurityTokenDVP(IbetExchangeInterface): + """IbetSecurityTokenDVP contract""" + + def __init__(self, contract_address: str): + super().__init__( + contract_address=contract_address, contract_name="IbetSecurityTokenDVP" + ) + + async def create_delivery( + self, data: CreateDeliveryParams, tx_from: str, private_key: bytes + ): + """Create Delivery""" + try: + tx = await self.exchange_contract.functions.createDelivery( + data.token_address, + data.buyer_address, + data.amount, + data.agent_address, + data.data, + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": tx_from, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash, tx_receipt = await AsyncContractUtils.send_transaction( + transaction=tx, private_key=private_key + ) + return tx_hash, tx_receipt + except ContractRevertError: + raise + except TimeExhausted as timeout_error: + raise SendTransactionError(timeout_error) + except Exception as err: + raise SendTransactionError(err) + + async def cancel_delivery( + self, data: CancelDeliveryParams, tx_from: str, private_key: bytes + ): + """Cancel Delivery""" + try: + tx = await self.exchange_contract.functions.cancelDelivery( + data.delivery_id + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": tx_from, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash, tx_receipt = await AsyncContractUtils.send_transaction( + transaction=tx, private_key=private_key + ) + return tx_hash, tx_receipt + except ContractRevertError: + raise + except TimeExhausted as timeout_error: + raise SendTransactionError(timeout_error) + except Exception as err: + raise SendTransactionError(err) + + async def finish_delivery( + self, data: FinishDeliveryParams, tx_from: str, private_key: bytes + ): + """Finish Delivery""" + try: + tx = await self.exchange_contract.functions.finishDelivery( + data.delivery_id + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": tx_from, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash, tx_receipt = await AsyncContractUtils.send_transaction( + transaction=tx, private_key=private_key + ) + return tx_hash, tx_receipt + except ContractRevertError: + raise + except TimeExhausted as timeout_error: + raise SendTransactionError(timeout_error) + except Exception as err: + raise SendTransactionError(err) + + async def abort_delivery( + self, data: AbortDeliveryParams, tx_from: str, private_key: bytes + ): + """Abort Delivery""" + try: + tx = await self.exchange_contract.functions.abortDelivery( + data.delivery_id + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": tx_from, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash, tx_receipt = await AsyncContractUtils.send_transaction( + transaction=tx, private_key=private_key + ) + return tx_hash, tx_receipt + except ContractRevertError: + raise + except TimeExhausted as timeout_error: + raise SendTransactionError(timeout_error) + except Exception as err: + raise SendTransactionError(err) diff --git a/app/model/blockchain/personal_info.py b/app/model/blockchain/personal_info.py index e1c02306..639fed49 100644 --- a/app/model/blockchain/personal_info.py +++ b/app/model/blockchain/personal_info.py @@ -24,6 +24,7 @@ from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA from eth_keyfile import decode_keyfile_json +from pydantic import BaseModel from web3.contract import AsyncContract from web3.exceptions import TimeExhausted @@ -37,6 +38,17 @@ web3 = Web3Wrapper() +class ContractPersonalInfoType(BaseModel): + key_manager: str | None = None + name: str | None = None + address: str | None = None + postal_code: str | None = None + email: str | None = None + birth: str | None = None + is_corporate: bool | None = None + tax_category: int | None = None + + class PersonalInfoContract: """PersonalInfo contract""" @@ -48,27 +60,16 @@ def __init__(self, issuer: Account, contract_address=None): contract_name="PersonalInfo", contract_address=contract_address ) self.issuer = issuer + self.cipher = None async def get_info(self, account_address: str, default_value=None): - """Get personal information + """Get personal information from contract storage :param account_address: Token holder account address :param default_value: Default value for items for which no value is set. (If not specified: None) :return: Personal info """ - # Set default value - personal_info = { - "key_manager": default_value, - "name": default_value, - "postal_code": default_value, - "address": default_value, - "email": default_value, - "birth": default_value, - "is_corporate": default_value, - "tax_category": default_value, - } - # Get encrypted personal information personal_info_state = await AsyncContractUtils.call_function( contract=self.personal_info_contract, @@ -82,18 +83,32 @@ async def get_info(self, account_address: str, default_value=None): encrypted_info = personal_info_state[2] if encrypted_info == "": - return personal_info # default + return ContractPersonalInfoType( + key_manager=default_value, + name=default_value, + address=default_value, + postal_code=default_value, + email=default_value, + birth=default_value, + ).model_dump() else: # Get issuer's RSA private key try: - passphrase = E2EEUtils.decrypt(self.issuer.rsa_passphrase) - key = RSA.importKey(self.issuer.rsa_private_key, passphrase) - cipher = PKCS1_OAEP.new(key) + if self.cipher is None: + passphrase = E2EEUtils.decrypt(self.issuer.rsa_passphrase) + key = RSA.importKey(self.issuer.rsa_private_key, passphrase) + self.cipher = PKCS1_OAEP.new(key) except Exception as err: logging.error(f"Cannot open the private key: {err}") - return personal_info # default - - if cipher is not None: + return ContractPersonalInfoType( + key_manager=default_value, + name=default_value, + address=default_value, + postal_code=default_value, + email=default_value, + birth=default_value, + ).model_dump() + if self.cipher is not None: try: ciphertext = base64.decodebytes(encrypted_info.encode("utf-8")) # NOTE: @@ -104,30 +119,28 @@ async def get_info(self, account_address: str, default_value=None): if len(ciphertext) == 1279: hex_fixed = "00" + ciphertext.hex() ciphertext = base64.b16decode(hex_fixed.upper()) - decrypted_info = json.loads(cipher.decrypt(ciphertext)) - - personal_info["key_manager"] = decrypted_info.get( - "key_manager", default_value - ) - personal_info["name"] = decrypted_info.get("name", default_value) - personal_info["address"] = decrypted_info.get( - "address", default_value - ) - personal_info["postal_code"] = decrypted_info.get( - "postal_code", default_value - ) - personal_info["email"] = decrypted_info.get("email", default_value) - personal_info["birth"] = decrypted_info.get("birth", default_value) - personal_info["is_corporate"] = decrypted_info.get( - "is_corporate", default_value - ) - personal_info["tax_category"] = decrypted_info.get( - "tax_category", default_value - ) - return personal_info + decrypted_info = json.loads(self.cipher.decrypt(ciphertext)) + + return ContractPersonalInfoType( + key_manager=decrypted_info.get("key_manager", default_value), + name=decrypted_info.get("name", default_value), + address=decrypted_info.get("address", default_value), + postal_code=decrypted_info.get("postal_code", default_value), + email=decrypted_info.get("email", default_value), + birth=decrypted_info.get("birth", default_value), + is_corporate=decrypted_info.get("is_corporate", None), + tax_category=decrypted_info.get("tax_category", None), + ).model_dump() except Exception as err: logging.error(f"Failed to decrypt: {err}") - return personal_info # default + return ContractPersonalInfoType( + key_manager=default_value, + name=default_value, + address=default_value, + postal_code=default_value, + email=default_value, + birth=default_value, + ).model_dump() async def register_info( self, account_address: str, data: dict, default_value=None diff --git a/app/model/blockchain/token.py b/app/model/blockchain/token.py index 8b1c8952..559ae250 100644 --- a/app/model/blockchain/token.py +++ b/app/model/blockchain/token.py @@ -18,7 +18,7 @@ """ import json -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from decimal import Decimal from random import randint from typing import List, TypeVar @@ -113,15 +113,17 @@ async def check_attr_update( async def record_attr_update(self, db_session: AsyncSession): _token_attr_update = TokenAttrUpdate() _token_attr_update.token_address = self.token_address - _token_attr_update.updated_datetime = datetime.utcnow() + _token_attr_update.updated_datetime = datetime.now(UTC).replace(tzinfo=None) db_session.add(_token_attr_update) async def create_cache(self, db_session: AsyncSession): token_cache = TokenCache() token_cache.token_address = self.token_address token_cache.attributes = self.__dict__ - token_cache.cached_datetime = datetime.utcnow() - token_cache.expiration_datetime = datetime.utcnow() + timedelta( + token_cache.cached_datetime = datetime.now(UTC).replace(tzinfo=None) + token_cache.expiration_datetime = datetime.now(UTC).replace( + tzinfo=None + ) + timedelta( seconds=randint( TOKEN_CACHE_TTL - TOKEN_CACHE_TTL_JITTER, TOKEN_CACHE_TTL + TOKEN_CACHE_TTL_JITTER, @@ -165,6 +167,7 @@ async def get_account_balance(self, account_address: str): class IbetSecurityTokenInterface(IbetStandardTokenInterface): personal_info_contract_address: str + require_personal_info_registered: bool transferable: bool is_offering: bool transfer_approval_required: bool @@ -513,7 +516,8 @@ async def get(self): ) if ( is_updated is False - and token_cache.expiration_datetime > datetime.utcnow() + and token_cache.expiration_datetime + > datetime.now(UTC).replace(tzinfo=None) ): # Get data from cache for k, v in token_cache.attributes.items(): @@ -550,6 +554,9 @@ async def get(self): AsyncContractUtils.call_function( contract, "personalInfoAddress", (), ZERO_ADDRESS ), + AsyncContractUtils.call_function( + contract, "requirePersonalInfoRegistered", (), True + ), AsyncContractUtils.call_function( contract, "transferable", (), False ), @@ -596,6 +603,7 @@ async def get(self): self.privacy_policy, self.status, self.personal_info_contract_address, + self.require_personal_info_registered, self.transferable, self.is_offering, self.transfer_approval_required, @@ -967,6 +975,28 @@ async def update( except Exception as err: raise SendTransactionError(err) + if data.require_personal_info_registered is not None: + tx = await contract.functions.setRequirePersonalInfoRegistered( + data.require_personal_info_registered + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": tx_from, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + try: + await AsyncContractUtils.send_transaction( + transaction=tx, private_key=private_key + ) + except ContractRevertError: + raise + except TimeExhausted as timeout_error: + raise SendTransactionError(timeout_error) + except Exception as err: + raise SendTransactionError(err) + if data.contact_information is not None: tx = await contract.functions.setContactInformation( data.contact_information @@ -1122,7 +1152,8 @@ async def get(self) -> T: ) if ( is_updated is False - and token_cache.expiration_datetime > datetime.utcnow() + and token_cache.expiration_datetime + > datetime.now(UTC).replace(tzinfo=None) ): # Get data from cache for k, v in token_cache.attributes.items(): @@ -1159,6 +1190,9 @@ async def get(self) -> T: AsyncContractUtils.call_function( contract, "personalInfoAddress", (), ZERO_ADDRESS ), + AsyncContractUtils.call_function( + contract, "requirePersonalInfoRegistered", (), True + ), AsyncContractUtils.call_function( contract, "transferable", (), False ), @@ -1189,6 +1223,7 @@ async def get(self) -> T: self.privacy_policy, self.status, self.personal_info_contract_address, + self.require_personal_info_registered, self.transferable, self.is_offering, self.transfer_approval_required, @@ -1272,6 +1307,28 @@ async def update( except Exception as err: raise SendTransactionError(err) + if data.require_personal_info_registered is not None: + tx = await contract.functions.setRequirePersonalInfoRegistered( + data.require_personal_info_registered + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": tx_from, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + try: + await AsyncContractUtils.send_transaction( + transaction=tx, private_key=private_key + ) + except ContractRevertError: + raise + except TimeExhausted as timeout_error: + raise SendTransactionError(timeout_error) + except Exception as err: + raise SendTransactionError(err) + if data.dividends is not None: _dividends = int(Decimal(str(data.dividends)) * Decimal("10000000000000")) tx = await contract.functions.setDividendInformation( diff --git a/app/model/blockchain/tx_params/ibet_security_token_dvp.py b/app/model/blockchain/tx_params/ibet_security_token_dvp.py new file mode 100644 index 00000000..de05f1ed --- /dev/null +++ b/app/model/blockchain/tx_params/ibet_security_token_dvp.py @@ -0,0 +1,40 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from pydantic import BaseModel + + +class CreateDeliveryParams(BaseModel): + token_address: str + buyer_address: str + amount: int + agent_address: str + data: str + + +class CancelDeliveryParams(BaseModel): + delivery_id: int + + +class FinishDeliveryParams(BaseModel): + delivery_id: int + + +class AbortDeliveryParams(BaseModel): + delivery_id: int diff --git a/app/model/blockchain/tx_params/ibet_share.py b/app/model/blockchain/tx_params/ibet_share.py index 3edcd27d..71dc0524 100644 --- a/app/model/blockchain/tx_params/ibet_share.py +++ b/app/model/blockchain/tx_params/ibet_share.py @@ -44,6 +44,7 @@ class UpdateParams(BaseModel): dividends: Optional[float] = None tradable_exchange_contract_address: Optional[EthereumAddress] = None personal_info_contract_address: Optional[EthereumAddress] = None + require_personal_info_registered: Optional[bool] = None transferable: Optional[bool] = None status: Optional[bool] = None is_offering: Optional[bool] = None diff --git a/app/model/blockchain/tx_params/ibet_straight_bond.py b/app/model/blockchain/tx_params/ibet_straight_bond.py index 6dd23437..28b734c9 100644 --- a/app/model/blockchain/tx_params/ibet_straight_bond.py +++ b/app/model/blockchain/tx_params/ibet_straight_bond.py @@ -52,6 +52,7 @@ class UpdateParams(BaseModel): is_redeemed: Optional[bool] = None tradable_exchange_contract_address: Optional[EthereumAddress] = None personal_info_contract_address: Optional[EthereumAddress] = None + require_personal_info_registered: Optional[bool] = None contact_information: Optional[str] = None privacy_policy: Optional[str] = None transfer_approval_required: Optional[bool] = None diff --git a/app/model/db/__init__.py b/app/model/db/__init__.py index cc4b0fe9..46b55f8a 100644 --- a/app/model/db/__init__.py +++ b/app/model/db/__init__.py @@ -31,9 +31,11 @@ BatchRegisterPersonalInfoUploadStatus, ) from .bulk_transfer import BulkTransfer, BulkTransferUpload +from .dvp_agent_account import DVPAgentAccount from .e2e_messaging_account import E2EMessagingAccount, E2EMessagingAccountRsaKey from .freeze_log_account import FreezeLogAccount from .idx_block_data import IDXBlockData, IDXBlockDataBlockNumber +from .idx_delivery import DeliveryStatus, IDXDelivery, IDXDeliveryBlockNumber from .idx_e2e_messaging import IDXE2EMessaging, IDXE2EMessagingBlockNumber from .idx_issue_redeem import ( IDXIssueRedeem, @@ -42,7 +44,12 @@ IDXIssueRedeemSortItem, ) from .idx_lock_unlock import IDXLock, IDXUnlock -from .idx_personal_info import IDXPersonalInfo, IDXPersonalInfoBlockNumber +from .idx_personal_info import ( + IDXPersonalInfo, + IDXPersonalInfoBlockNumber, + IDXPersonalInfoHistory, + PersonalInfoEventType, +) from .idx_position import ( IDXLockedPosition, IDXPosition, diff --git a/app/model/db/auth_token.py b/app/model/db/auth_token.py index a74d5073..5c69a6f7 100644 --- a/app/model/db/auth_token.py +++ b/app/model/db/auth_token.py @@ -22,7 +22,7 @@ from sqlalchemy import DateTime, Integer, String from sqlalchemy.orm import Mapped, mapped_column -from .base import Base +from .base import Base, naive_utcnow class AuthToken(Base): @@ -35,9 +35,7 @@ class AuthToken(Base): # authentication token (sha256 hashed) auth_token: Mapped[str | None] = mapped_column(String(64)) # usage start - usage_start: Mapped[datetime | None] = mapped_column( - DateTime, default=datetime.utcnow - ) + usage_start: Mapped[datetime | None] = mapped_column(DateTime, default=naive_utcnow) # valid duration (sec) # - 0: endless valid_duration: Mapped[int | None] = mapped_column(Integer, nullable=False) diff --git a/app/model/db/base.py b/app/model/db/base.py index 43b92789..80f95571 100644 --- a/app/model/db/base.py +++ b/app/model/db/base.py @@ -18,20 +18,28 @@ """ import time -from datetime import date as datetime_date, datetime +from datetime import UTC, date as datetime_date, datetime from sqlalchemy import DateTime -from sqlalchemy.orm import Mapped, declarative_base, mapped_column +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column from app.database import get_db_schema -class BaseModel(object): +def aware_utcnow(): + return datetime.now(UTC) + + +def naive_utcnow(): + return aware_utcnow().replace(tzinfo=None) + + +class Base(DeclarativeBase): # created datetime(UTC) - created: Mapped[datetime | None] = mapped_column(DateTime, default=datetime.utcnow) + created: Mapped[datetime | None] = mapped_column(DateTime, default=naive_utcnow) # modified datetime(UTC) modified: Mapped[datetime | None] = mapped_column( - DateTime, default=datetime.utcnow, onupdate=datetime.utcnow + DateTime, default=naive_utcnow, onupdate=naive_utcnow ) @staticmethod @@ -42,8 +50,6 @@ def datetime_to_timestamp(date): return None -Base = declarative_base(cls=BaseModel) - schema = get_db_schema() if schema is not None: setattr(Base, "__table_args__", {"schema": schema}) diff --git a/app/model/db/dvp_agent_account.py b/app/model/db/dvp_agent_account.py new file mode 100644 index 00000000..a0150928 --- /dev/null +++ b/app/model/db/dvp_agent_account.py @@ -0,0 +1,38 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from sqlalchemy import JSON, Boolean, String +from sqlalchemy.orm import Mapped, mapped_column + +from .base import Base + + +class DVPAgentAccount(Base): + """Account for DVP payment agent""" + + __tablename__ = "dvp_agent_account" + + # account address + account_address: Mapped[str] = mapped_column(String(42), primary_key=True) + # ethereum keyfile + keyfile: Mapped[dict | None] = mapped_column(JSON) + # ethereum account password(encrypted) + eoa_password: Mapped[str | None] = mapped_column(String(2000)) + # delete flag + is_deleted: Mapped[bool | None] = mapped_column(Boolean, default=False) diff --git a/app/model/db/idx_delivery.py b/app/model/db/idx_delivery.py new file mode 100644 index 00000000..8cec4553 --- /dev/null +++ b/app/model/db/idx_delivery.py @@ -0,0 +1,129 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from datetime import datetime +from enum import IntEnum + +from sqlalchemy import BigInteger, Boolean, DateTime, String, Text +from sqlalchemy.orm import Mapped, mapped_column + +from .base import Base + + +class DeliveryStatus(IntEnum): + """DVP Delivery Status""" + + DELIVERY_CREATED = 0 + DELIVERY_CANCELED = 1 + DELIVERY_CONFIRMED = 2 + DELIVERY_FINISHED = 3 + DELIVERY_ABORTED = 4 + + +class IDXDelivery(Base): + """DVP Delivery Event (INDEX)""" + + __tablename__ = "idx_delivery" + + # Sequence Id + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + # DVP Contract Address + exchange_address: Mapped[str] = mapped_column( + String(42), index=True, nullable=False + ) + # Delivery ID + delivery_id: Mapped[int] = mapped_column(BigInteger, index=True, nullable=False) + # Token Address + token_address: Mapped[str] = mapped_column(String(42), index=True, nullable=False) + # Delivery Buyer + buyer_address: Mapped[str] = mapped_column(String(42), nullable=False) + # Delivery From + seller_address: Mapped[str] = mapped_column(String(42), index=True, nullable=False) + # Delivery Amount + amount: Mapped[int] = mapped_column(BigInteger, nullable=False) + # Delivery Agent + agent_address: Mapped[str] = mapped_column(String(42), index=True, nullable=False) + # Data + data: Mapped[str] = mapped_column(Text) + # Create Delivery Blocktimestamp + create_blocktimestamp: Mapped[datetime] = mapped_column(DateTime, nullable=False) + # Create Transaction Hash + create_transaction_hash: Mapped[str] = mapped_column( + String(66), index=True, nullable=False + ) + # Cancel Delivery Blocktimestamp + cancel_blocktimestamp: Mapped[datetime | None] = mapped_column(DateTime) + # Cancel Transaction Hash + cancel_transaction_hash: Mapped[str | None] = mapped_column(String(66), index=True) + # Confirm Delivery Blocktimestamp + confirm_blocktimestamp: Mapped[datetime | None] = mapped_column(DateTime) + # Confirm Transaction Hash + confirm_transaction_hash: Mapped[str | None] = mapped_column(String(66), index=True) + # Finish Delivery Blocktimestamp + finish_blocktimestamp: Mapped[datetime | None] = mapped_column(DateTime) + # Finish Transaction Hash + finish_transaction_hash: Mapped[str | None] = mapped_column(String(66), index=True) + # Abort Delivery Blocktimestamp + abort_blocktimestamp: Mapped[datetime | None] = mapped_column(DateTime) + # Abort Transaction Hash + abort_transaction_hash: Mapped[str | None] = mapped_column(String(66), index=True) + # Confirmation Status + confirmed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) + # Delivery Valid Status + valid: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) + # Delivery Status(DeliveryStatus) + status: Mapped[int] = mapped_column( + BigInteger, nullable=False, default=DeliveryStatus.DELIVERY_CREATED + ) + + def json(self): + return { + "exchange_address": self.exchange_address, + "token_address": self.token_address, + "delivery_id": self.delivery_id, + "buyer_address": self.buyer_address, + "seller_address": self.seller_address, + "agent_address": self.agent_address, + "amount": self.amount, + "data": self.data, + "create_blocktimestamp": self.create_blocktimestamp, + "create_transaction_hash": self.create_transaction_hash, + "cancel_blocktimestamp": self.cancel_blocktimestamp, + "cancel_transaction_hash": self.cancel_transaction_hash, + "confirm_blocktimestamp": self.confirm_blocktimestamp, + "confirm_transaction_hash": self.confirm_transaction_hash, + "finish_blocktimestamp": self.finish_blocktimestamp, + "finish_transaction_hash": self.finish_transaction_hash, + "abort_blocktimestamp": self.abort_blocktimestamp, + "abort_transaction_hash": self.abort_transaction_hash, + "confirmed": self.confirmed, + "valid": self.valid, + "status": self.status, + } + + +class IDXDeliveryBlockNumber(Base): + """Synchronized blockNumber of IDXDelivery""" + + __tablename__ = "idx_delivery_block_number" + + # target exchange address + exchange_address: Mapped[str] = mapped_column(String(42), primary_key=True) + # latest blockNumber + latest_block_number: Mapped[int] = mapped_column(BigInteger, nullable=False) diff --git a/app/model/db/idx_personal_info.py b/app/model/db/idx_personal_info.py index 14511b76..03e0d568 100644 --- a/app/model/db/idx_personal_info.py +++ b/app/model/db/idx_personal_info.py @@ -17,12 +17,21 @@ SPDX-License-Identifier: Apache-2.0 """ -from sqlalchemy import JSON, BigInteger, String +from datetime import datetime +from enum import StrEnum + +import pytz +from sqlalchemy import JSON, BigInteger, DateTime, String from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import Mapped, mapped_column +import config + from .base import Base +local_tz = pytz.timezone(config.TZ) +utc_tz = pytz.timezone("UTC") + class IDXPersonalInfo(Base): """INDEX Personal information of the token holder (decrypted)""" @@ -75,13 +84,19 @@ def _personal_info_setter(self, personal_info_dict: dict): "tax_category": personal_info_dict.get("tax_category", None), } + @staticmethod + def localize_datetime(_datetime: datetime) -> datetime | None: + if _datetime is None: + return None + return utc_tz.localize(_datetime).astimezone(local_tz) + def json(self): return { "id": self.id, "account_address": self.account_address, - "issuer_address": self.issuer_address, "personal_info": self.personal_info, - "created": self.created, + "created": self.localize_datetime(self.created), + "modified": self.localize_datetime(self.modified), } @@ -93,3 +108,44 @@ class IDXPersonalInfoBlockNumber(Base): id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) # latest blockNumber latest_block_number: Mapped[int | None] = mapped_column(BigInteger) + + +class PersonalInfoEventType(StrEnum): + REGISTER = "register" + MODIFY = "modify" + + +class IDXPersonalInfoHistory(Base): + """Indexed personal information histories""" + + __tablename__ = "idx_personal_info_history" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + # account address + account_address: Mapped[str | None] = mapped_column(String(42), index=True) + # issuer address + issuer_address: Mapped[str | None] = mapped_column(String(42), index=True) + # event type + event_type: Mapped[PersonalInfoEventType] = mapped_column( + String(10), index=True, nullable=False + ) + # personal information + personal_info = mapped_column(JSON, nullable=False) + # block timestamp + block_timestamp: Mapped[datetime | None] = mapped_column(DateTime) + + @staticmethod + def localize_datetime(_datetime: datetime) -> datetime | None: + if _datetime is None: + return None + return utc_tz.localize(_datetime).astimezone(local_tz) + + def json(self): + return { + "id": self.id, + "account_address": self.account_address, + "event_type": self.event_type, + "personal_info": self.personal_info, + "block_timestamp": self.localize_datetime(self.block_timestamp), + "created": self.localize_datetime(self.created), + } diff --git a/app/model/db/ledger.py b/app/model/db/ledger.py index c8e976ed..51417c4c 100644 --- a/app/model/db/ledger.py +++ b/app/model/db/ledger.py @@ -22,7 +22,7 @@ from sqlalchemy import JSON, BigInteger, DateTime, Integer, String from sqlalchemy.orm import Mapped, mapped_column -from .base import Base +from .base import Base, naive_utcnow class Ledger(Base): @@ -41,7 +41,7 @@ class Ledger(Base): # created datetime(UTC) # NOTE: Because Base's created column is subject to change in the data patch, define another column. ledger_created: Mapped[datetime] = mapped_column( - DateTime, nullable=False, default=datetime.utcnow + DateTime, nullable=False, default=naive_utcnow ) @@ -69,6 +69,7 @@ class Ledger(Base): } ], "footers": [], + "some_personal_info_not_registered": "boolean", }, ], "footers": [], @@ -102,5 +103,5 @@ class LedgerDetailsData(Base): # created datetime(UTC) # NOTE: Because Base's created column is subject to change in the data patch, define another column. data_created: Mapped[datetime] = mapped_column( - DateTime, nullable=False, default=datetime.utcnow + DateTime, nullable=False, default=naive_utcnow ) diff --git a/app/model/db/token.py b/app/model/db/token.py index 7f92a81d..d6b0c7a9 100644 --- a/app/model/db/token.py +++ b/app/model/db/token.py @@ -23,7 +23,7 @@ from sqlalchemy import JSON, Boolean, DateTime, Integer, String from sqlalchemy.orm import Mapped, mapped_column -from .base import Base +from .base import Base, naive_utcnow class TokenType(str, Enum): @@ -34,6 +34,7 @@ class TokenType(str, Enum): class TokenVersion(StrEnum): V_22_12 = "22_12" V_23_12 = "23_12" + V_24_06 = "24_06" class Token(Base): @@ -84,9 +85,9 @@ class TokenCache(Base): attributes: Mapped[dict] = mapped_column(JSON, nullable=False) # cached datetime cached_datetime: Mapped[datetime | None] = mapped_column( - DateTime, default=datetime.utcnow + DateTime, default=naive_utcnow ) # expiration datetime expiration_datetime: Mapped[datetime | None] = mapped_column( - DateTime, default=datetime.utcnow + DateTime, default=naive_utcnow ) diff --git a/app/model/schema/__init__.py b/app/model/schema/__init__.py index 57506363..0815f057 100644 --- a/app/model/schema/__init__.py +++ b/app/model/schema/__init__.py @@ -113,6 +113,19 @@ ScheduledEventIdResponse, ScheduledEventResponse, ) +from .settlement import ( + AbortDVPDeliveryRequest, + CancelDVPDeliveryRequest, + CreateDVPAgentAccountRequest, + CreateDVPDeliveryRequest, + DVPAgentAccountChangeEOAPasswordRequest, + DVPAgentAccountResponse, + FinishDVPDeliveryRequest, + ListAllDVPAgentAccountResponse, + ListAllDVPDeliveriesQuery, + ListAllDVPDeliveriesResponse, + RetrieveDVPDeliveryResponse, +) from .token import ( IbetShareAdditionalIssue, IbetShareCreate, @@ -145,6 +158,8 @@ CreateTokenHoldersListRequest, CreateTokenHoldersListResponse, ListAllTokenHolderCollectionsResponse, + ListTokenHoldersPersonalInfoHistoryQuery, + ListTokenHoldersPersonalInfoHistoryResponse, ListTokenHoldersPersonalInfoQuery, ListTokenHoldersPersonalInfoResponse, RetrieveTokenHoldersListResponse, diff --git a/app/model/schema/base/base.py b/app/model/schema/base/base.py index 34abe21f..5d1902df 100644 --- a/app/model/schema/base/base.py +++ b/app/model/schema/base/base.py @@ -30,10 +30,12 @@ class IbetStraightBondContractVersion(StrEnum): V_22_12 = "22_12" V_23_12 = "23_12" + V_24_06 = "24_06" class IbetShareContractVersion(StrEnum): V_22_12 = "22_12" + V_24_06 = "24_06" MMDD_constr = Annotated[ diff --git a/app/model/schema/ledger.py b/app/model/schema/ledger.py index 917aaa1f..740fd58c 100644 --- a/app/model/schema/ledger.py +++ b/app/model/schema/ledger.py @@ -126,6 +126,7 @@ class RetrieveLedgerDetailsHistoryResponse(BaseModel): headers: Optional[List[dict]] = None data: List[RetrieveLedgerDetailsDataHistoryResponse] footers: Optional[List[dict]] = None + some_personal_info_not_registered: bool class RetrieveLedgerHistoryResponse(BaseModel): diff --git a/app/model/schema/personal_info.py b/app/model/schema/personal_info.py index eda7142e..8226508d 100644 --- a/app/model/schema/personal_info.py +++ b/app/model/schema/personal_info.py @@ -18,6 +18,7 @@ """ from datetime import datetime +from enum import StrEnum from typing import Annotated, List, Optional from fastapi import Query @@ -30,6 +31,11 @@ from .base import ResultSet, SortOrder +class PersonalInfoEventType(StrEnum): + REGISTER = "register" + MODIFY = "modify" + + class PersonalInfo(BaseModel): key_manager: Optional[str] = Field(...) name: Optional[str] = Field(...) @@ -60,6 +66,18 @@ class PersonalInfoIndex(BaseModel): account_address: str = Field(...) personal_info: PersonalInfo = Field(...) created: datetime + modified: datetime + + +class PersonalInfoHistory(BaseModel): + """Personal Information History schema""" + + id: int = Field(...) + account_address: str = Field(...) + event_type: PersonalInfoEventType = Field(...) + personal_info: PersonalInfo = Field(...) + block_timestamp: datetime = Field(...) + created: datetime ############################ diff --git a/app/model/schema/settlement.py b/app/model/schema/settlement.py new file mode 100644 index 00000000..c450a8e2 --- /dev/null +++ b/app/model/schema/settlement.py @@ -0,0 +1,187 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from datetime import datetime +from enum import IntEnum +from typing import Annotated, Literal, Optional + +from fastapi import Query +from pydantic import BaseModel, Field, RootModel, field_validator +from pydantic.dataclasses import dataclass + +from app.model import EthereumAddress +from app.model.schema.base import ResultSet, SortOrder +from app.utils.check_utils import check_value_is_encrypted +from config import E2EE_REQUEST_ENABLED + +############################ +# COMMON +############################ + + +class DeliveryStatus(IntEnum): + """DVP Delivery Status""" + + DELIVERY_CREATED = 0 + DELIVERY_CANCELED = 1 + DELIVERY_CONFIRMED = 2 + DELIVERY_FINISHED = 3 + DELIVERY_ABORTED = 4 + + +############################ +# REQUEST +############################ +class CreateDVPAgentAccountRequest(BaseModel): + """DVP agent account create schema (REQUEST)""" + + eoa_password: str = Field(..., description="EOA keyfile password") + + @field_validator("eoa_password") + @classmethod + def eoa_password_is_encrypted_value(cls, v): + if E2EE_REQUEST_ENABLED: + check_value_is_encrypted("eoa_password", v) + return v + + +class DVPAgentAccountChangeEOAPasswordRequest(BaseModel): + """DVP agent account change EOA password schema (REQUEST)""" + + old_eoa_password: str = Field(..., description="EOA keyfile password (old)") + eoa_password: str = Field(..., description="EOA keyfile password (new)") + + @field_validator("old_eoa_password") + @classmethod + def old_eoa_password_is_encrypted_value(cls, v): + if E2EE_REQUEST_ENABLED: + check_value_is_encrypted("old_eoa_password", v) + return v + + @field_validator("eoa_password") + @classmethod + def eoa_password_is_encrypted_value(cls, v): + if E2EE_REQUEST_ENABLED: + check_value_is_encrypted("eoa_password", v) + return v + + +@dataclass +class ListAllDVPDeliveriesQuery: + token_address: Annotated[Optional[str], Query(description="Token address")] = None + seller_address: Annotated[Optional[str], Query(description="Seller address")] = None + buyer_address: Annotated[Optional[str], Query(description="Buyer address")] = None + agent_address: Annotated[Optional[str], Query(description="Agent address")] = None + valid: Annotated[Optional[bool], Query(description="Valid flag")] = None + status: Annotated[ + Optional[DeliveryStatus], Query(description="Delivery status") + ] = None + create_blocktimestamp_from: Annotated[ + Optional[datetime], Query(description="Create block timestamp filter(From)") + ] = None + create_blocktimestamp_to: Annotated[ + Optional[datetime], Query(description="Create block timestamp filter(To)") + ] = None + + sort_order: Annotated[SortOrder, Query(description="0:asc, 1:desc")] = ( + SortOrder.DESC + ) + offset: Annotated[Optional[int], Query(description="Start position", ge=0)] = None + limit: Annotated[Optional[int], Query(description="Number of set", ge=0)] = None + + +class CreateDVPDeliveryRequest(BaseModel): + """DVP delivery create schema (REQUEST)""" + + token_address: EthereumAddress + buyer_address: EthereumAddress + amount: int = Field(..., ge=1, le=1_000_000_000_000) + agent_address: EthereumAddress + data: str + + +class CancelDVPDeliveryRequest(BaseModel): + """DVP delivery cancel schema (REQUEST)""" + + operation_type: Literal["Cancel"] + + +class FinishDVPDeliveryRequest(BaseModel): + """DVP delivery finish schema (REQUEST)""" + + operation_type: Literal["Finish"] + account_address: EthereumAddress = Field(..., description="Agent account address") + eoa_password: str = Field(..., description="Agent account key file password") + + +class AbortDVPDeliveryRequest(BaseModel): + """DVP delivery abort schema (REQUEST)""" + + operation_type: Literal["Abort"] + account_address: EthereumAddress = Field(..., description="Agent account address") + eoa_password: str = Field(..., description="Agent account key file password") + + +############################ +# RESPONSE +############################ +class DVPAgentAccountResponse(BaseModel): + """DVP agent account reference schema (RESPONSE)""" + + account_address: str + is_deleted: bool + + +class ListAllDVPAgentAccountResponse(RootModel[list[DVPAgentAccountResponse]]): + """DVP agent account list reference schema (RESPONSE)""" + + pass + + +class RetrieveDVPDeliveryResponse(BaseModel): + """Retrieve DVP delivery schema (Response)""" + + exchange_address: str + delivery_id: int + token_address: str + buyer_address: str + seller_address: str + amount: int + agent_address: str + data: str = Field(examples=["{}", '{"type": "primary"}']) + create_blocktimestamp: str + create_transaction_hash: str + cancel_blocktimestamp: Optional[str] = Field(...) + cancel_transaction_hash: Optional[str] = Field(...) + confirm_blocktimestamp: Optional[str] = Field(...) + confirm_transaction_hash: Optional[str] = Field(...) + finish_blocktimestamp: Optional[str] = Field(...) + finish_transaction_hash: Optional[str] = Field(...) + abort_blocktimestamp: Optional[str] = Field(...) + abort_transaction_hash: Optional[str] = Field(...) + confirmed: bool + valid: bool + status: DeliveryStatus + + +class ListAllDVPDeliveriesResponse(BaseModel): + """List all DVP delivery schema (Response)""" + + result_set: ResultSet + deliveries: list[RetrieveDVPDeliveryResponse] diff --git a/app/model/schema/token.py b/app/model/schema/token.py index 65ccc721..0f4a47c8 100644 --- a/app/model/schema/token.py +++ b/app/model/schema/token.py @@ -27,7 +27,7 @@ from pydantic import BaseModel, Field, field_validator, model_validator from pydantic.dataclasses import dataclass -from app.model import EthereumAddress +from app.model import EthereumAddress, ValidatedDatetimeStr from .base import ( CURRENCY_str, @@ -74,6 +74,7 @@ class IbetStraightBondCreate(BaseModel): is_offering: Optional[bool] = None tradable_exchange_contract_address: Optional[EthereumAddress] = None personal_info_contract_address: Optional[EthereumAddress] = None + require_personal_info_registered: Optional[bool] = None image_url: Optional[list[str]] = None contact_information: Optional[str] = Field(default=None, max_length=2000) privacy_policy: Optional[str] = Field(default=None, max_length=5000) @@ -130,6 +131,7 @@ class IbetStraightBondUpdate(BaseModel): is_redeemed: Optional[bool] = None tradable_exchange_contract_address: Optional[EthereumAddress] = None personal_info_contract_address: Optional[EthereumAddress] = None + require_personal_info_registered: Optional[bool] = None contact_information: Optional[str] = Field(default=None, max_length=2000) privacy_policy: Optional[str] = Field(default=None, max_length=5000) transfer_approval_required: Optional[bool] = None @@ -214,6 +216,7 @@ class IbetShareCreate(BaseModel): is_offering: Optional[bool] = None tradable_exchange_contract_address: Optional[EthereumAddress] = None personal_info_contract_address: Optional[EthereumAddress] = None + require_personal_info_registered: Optional[bool] = None contact_information: Optional[str] = Field(default=None, max_length=2000) privacy_policy: Optional[str] = Field(default=None, max_length=5000) transfer_approval_required: Optional[bool] = None @@ -239,6 +242,7 @@ class IbetShareUpdate(BaseModel): dividends: Optional[float] = Field(default=None, ge=0.00, le=5_000_000_000.00) tradable_exchange_contract_address: Optional[EthereumAddress] = None personal_info_contract_address: Optional[EthereumAddress] = None + require_personal_info_registered: Optional[bool] = None transferable: Optional[bool] = None status: Optional[bool] = None is_offering: Optional[bool] = None @@ -359,6 +363,7 @@ class ListAllHoldersSortItem(StrEnum): balance = "balance" pending_transfer = "pending_transfer" locked = "locked" + balance_and_pending_transfer = "balance_and_pending_transfer" key_manager = "key_manager" holder_name = "holder_name" @@ -391,6 +396,16 @@ class ListAllHoldersQuery: description="search condition of locked amount(0:equal, 1:greater than or equal, 2:less than or equal)", ), ] = ValueOperator.EQUAL + balance_and_pending_transfer: Annotated[ + Optional[int], + Query(description="number of balance plus pending transfer amount"), + ] = None + balance_and_pending_transfer_operator: Annotated[ + Optional[ValueOperator], + Query( + description="search condition of balance plus pending transfer(0:equal, 1:greater than or equal, 2:less than or equal)", + ), + ] = ValueOperator.EQUAL account_address: Annotated[ Optional[str], Query(description="account address(partial match)") ] = None @@ -464,10 +479,10 @@ class ListTokenOperationLogHistoryQuery: Optional[TokenUpdateOperationCategory], Query(description="Trigger of change") ] = None created_from: Annotated[ - Optional[datetime], Query(description="Created datetime filter(From)") + Optional[ValidatedDatetimeStr], Query(description="created datetime (From)") ] = None created_to: Annotated[ - Optional[datetime], Query(description="Created datetime filter(To)") + Optional[ValidatedDatetimeStr], Query(description="created datetime (To)") ] = None sort_item: Annotated[ListTokenHistorySortItem, Query(description="Sort item")] = ( ListTokenHistorySortItem.created @@ -515,6 +530,7 @@ class IbetStraightBondResponse(BaseModel): is_offering: bool tradable_exchange_contract_address: str personal_info_contract_address: str + require_personal_info_registered: bool contact_information: str privacy_policy: str issue_datetime: str @@ -544,6 +560,7 @@ class IbetShareResponse(BaseModel): is_offering: bool tradable_exchange_contract_address: str personal_info_contract_address: str + require_personal_info_registered: bool contact_information: str privacy_policy: str issue_datetime: str diff --git a/app/model/schema/token_holders.py b/app/model/schema/token_holders.py index c03e9770..b72ee968 100644 --- a/app/model/schema/token_holders.py +++ b/app/model/schema/token_holders.py @@ -18,30 +18,99 @@ """ import uuid +from enum import StrEnum from typing import Annotated, List, Optional from fastapi import Query from pydantic import BaseModel, ConfigDict, Field, NonNegativeInt, field_validator +from pydantic.dataclasses import dataclass +from app.model import ValidatedDatetimeStr from app.model.db import TokenHolderBatchStatus from app.model.schema.base import ResultSet, SortOrder -from app.model.schema.personal_info import PersonalInfoIndex +from app.model.schema.personal_info import ( + PersonalInfoEventType, + PersonalInfoHistory, + PersonalInfoIndex, +) ############################ # REQUEST ############################ -class ListTokenHoldersPersonalInfoQuery(BaseModel): +class ListTokenHoldersPersonalInfoSortItem(StrEnum): + account_address = "account_address" + created = "created" + modified = "modified" + + +@dataclass +class ListTokenHoldersPersonalInfoQuery: + account_address: Annotated[Optional[str], Query(description="account address")] = ( + None + ) + created_from: Annotated[ + Optional[ValidatedDatetimeStr], Query(description="created datetime (From)") + ] = None + created_to: Annotated[ + Optional[ValidatedDatetimeStr], Query(description="created datetime (To)") + ] = None + modified_from: Annotated[ + Optional[ValidatedDatetimeStr], Query(description="modified datetime (From)") + ] = None + modified_to: Annotated[ + Optional[ValidatedDatetimeStr], Query(description="modified datetime (To)") + ] = None + + sort_item: Annotated[ + ListTokenHoldersPersonalInfoSortItem, Query(description="sort item") + ] = ListTokenHoldersPersonalInfoSortItem.created + sort_order: Annotated[ + Optional[SortOrder], Query(description="sort order(0: ASC, 1: DESC)") + ] = SortOrder.ASC + offset: Annotated[Optional[NonNegativeInt], Query(description="start position")] = ( None ) limit: Annotated[Optional[NonNegativeInt], Query(description="number of set")] = ( None ) + + +@dataclass +class ListTokenHoldersPersonalInfoHistoryQuery: + account_address: Annotated[Optional[str], Query(description="account address")] = ( + None + ) + event_type: Annotated[ + Optional[PersonalInfoEventType], Query(description="event type") + ] = None + block_timestamp_from: Annotated[ + Optional[ValidatedDatetimeStr], + Query(description="block timestamp datetime (From)"), + ] = None + block_timestamp_to: Annotated[ + Optional[ValidatedDatetimeStr], + Query(description="block timestamp datetime (To)"), + ] = None + created_from: Annotated[ + Optional[ValidatedDatetimeStr], Query(description="created datetime (From)") + ] = None + created_to: Annotated[ + Optional[ValidatedDatetimeStr], Query(description="created datetime (To)") + ] = None + sort_order: Annotated[ - Optional[SortOrder], Query(description="sort order(0: ASC, 1: DESC)") + Optional[SortOrder], Query(description="sort order (0: ASC, 1: DESC)") ] = SortOrder.ASC + offset: Annotated[Optional[NonNegativeInt], Query(description="start position")] = ( + None + ) + limit: Annotated[Optional[NonNegativeInt], Query(description="number of set")] = ( + None + ) + class CreateTokenHoldersListRequest(BaseModel): """Create Token Holders List schema (REQUEST)""" @@ -79,6 +148,13 @@ class ListTokenHoldersPersonalInfoResponse(BaseModel): personal_info: List[PersonalInfoIndex] +class ListTokenHoldersPersonalInfoHistoryResponse(BaseModel): + """List All Token Holders PersonalInfo Histories (Response)""" + + result_set: ResultSet + personal_info: List[PersonalInfoHistory] + + class CreateTokenHoldersListResponse(BaseModel): """Create Token Holders List schema (RESPONSE)""" diff --git a/app/model/schema/transfer.py b/app/model/schema/transfer.py index cfceb082..203811ea 100644 --- a/app/model/schema/transfer.py +++ b/app/model/schema/transfer.py @@ -17,14 +17,16 @@ SPDX-License-Identifier: Apache-2.0 """ +from datetime import datetime from enum import Enum, IntEnum from typing import Annotated, List, Optional from fastapi import Query -from pydantic import BaseModel, Field, NonNegativeInt, conint +from pydantic import BaseModel, Field, NonNegativeInt from pydantic.dataclasses import dataclass from app.model.schema.base import ResultSet, SortOrder +from app.model.schema.base.base import ValueOperator from .personal_info import PersonalInfo @@ -54,11 +56,38 @@ class ListTransferHistorySortItem(str, Enum): BLOCK_TIMESTAMP = "block_timestamp" FROM_ADDRESS = "from_address" TO_ADDRESS = "to_address" + FROM_ADDRESS_NAME = "from_address_name" + TO_ADDRESS_NAME = "to_address_name" AMOUNT = "amount" @dataclass class ListTransferHistoryQuery: + block_timestamp_from: Annotated[ + Optional[datetime], Query(description="block timestamp (From)") + ] = None + block_timestamp_to: Annotated[ + Optional[datetime], Query(descriptio0n="block timestamp (To)") + ] = None + from_address: Annotated[ + Optional[str], Query(description="transfer source address") + ] = None + to_address: Annotated[ + Optional[str], Query(description="transfer destination address") + ] = None + from_address_name: Annotated[ + Optional[str], Query(description="name of transfer source address") + ] = None + to_address_name: Annotated[ + Optional[str], Query(description="name of transfer destination address") + ] = None + amount: Annotated[Optional[int], Query(description="transfer amount")] = None + amount_operator: Annotated[ + Optional[ValueOperator], + Query( + description="value filter condition(0: equal, 1: greater than, 2: less than)", + ), + ] = ValueOperator.EQUAL source_event: Annotated[ Optional[TransferSourceEventType], Query(description="source event of transfer") ] = None diff --git a/app/routers/account.py b/app/routers/account.py index c80bf85e..35b98cf9 100644 --- a/app/routers/account.py +++ b/app/routers/account.py @@ -20,17 +20,17 @@ import hashlib import re import secrets -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from typing import List, Optional, Sequence import boto3 import eth_keyfile +import pytz from coincurve import PublicKey from Crypto.PublicKey import RSA from eth_utils import keccak, to_checksum_address from fastapi import APIRouter, Header, Request from fastapi.exceptions import HTTPException -from pytz import timezone from sqlalchemy import select from sqlalchemy.exc import IntegrityError as SAIntegrityError @@ -80,7 +80,7 @@ router = APIRouter(tags=["account"]) -local_tz = timezone(TZ) +local_tz = pytz.timezone(TZ) # POST: /accounts @@ -453,7 +453,9 @@ async def create_auth_token( hashed_token = hashlib.sha256(new_token.encode()).hexdigest() # Get current datetime - current_datetime_utc = timezone("UTC").localize(datetime.utcnow()) + current_datetime_utc = pytz.timezone("UTC").localize( + datetime.now(UTC).replace(tzinfo=None) + ) current_datetime_local = current_datetime_utc.astimezone(local_tz).isoformat() # Register auth token @@ -470,7 +472,7 @@ async def create_auth_token( expiration_datetime = auth_token.usage_start + timedelta( seconds=auth_token.valid_duration ) - if datetime.utcnow() <= expiration_datetime: + if datetime.now(UTC).replace(tzinfo=None) <= expiration_datetime: raise AuthTokenAlreadyExistsError() # Update auth token auth_token.auth_token = hashed_token diff --git a/app/routers/bond.py b/app/routers/bond.py index 6f682b38..f4867b86 100644 --- a/app/routers/bond.py +++ b/app/routers/bond.py @@ -18,13 +18,13 @@ """ import uuid -from datetime import datetime +from datetime import UTC, datetime from typing import List, Optional, Sequence +import pytz from eth_keyfile import decode_keyfile_json from fastapi import APIRouter, Depends, Header, Query, Request from fastapi.exceptions import HTTPException -from pytz import timezone from sqlalchemy import ( String, and_, @@ -41,6 +41,7 @@ select, ) from sqlalchemy.orm import aliased +from sqlalchemy.sql.functions import coalesce import config from app import log @@ -50,6 +51,7 @@ ContractRevertError, InvalidParameterError, OperationNotAllowedStateError, + OperationNotSupportedVersionError, SendTransactionError, ) from app.model.blockchain import ( @@ -168,8 +170,8 @@ ) LOG = log.get_logger() -local_tz = timezone(config.TZ) -utc_tz = timezone("UTC") +local_tz = pytz.timezone(config.TZ) +utc_tz = pytz.timezone("UTC") # POST: /bond/tokens @@ -258,6 +260,7 @@ async def issue_token( "is_redeemed", "tradable_exchange_contract_address", "personal_info_contract_address", + "require_personal_info_registered", "contact_information", "privacy_policy", "transfer_approval_required", @@ -312,7 +315,9 @@ async def issue_token( _utxo.token_address = contract_address _utxo.amount = token.total_supply _utxo.block_number = block["number"] - _utxo.block_timestamp = datetime.utcfromtimestamp(block["timestamp"]) + _utxo.block_timestamp = datetime.fromtimestamp(block["timestamp"], UTC).replace( + tzinfo=None + ) db.add(_utxo) token_status = 1 # succeeded @@ -325,7 +330,7 @@ async def issue_token( _token.token_address = contract_address _token.abi = abi _token.token_status = token_status - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # Register operation log @@ -389,7 +394,7 @@ async def list_all_tokens( bond_token = ( await IbetStraightBondContract(token.token_address).get() ).__dict__ - issue_datetime_utc = timezone("UTC").localize(token.created) + issue_datetime_utc = pytz.timezone("UTC").localize(token.created) bond_token["issue_datetime"] = issue_datetime_utc.astimezone( local_tz ).isoformat() @@ -430,7 +435,7 @@ async def retrieve_token(db: DBAsyncSession, token_address: str): # Get contract data bond_token = (await IbetStraightBondContract(token_address).get()).__dict__ - issue_datetime_utc = timezone("UTC").localize(_token.created) + issue_datetime_utc = pytz.timezone("UTC").localize(_token.created) bond_token["issue_datetime"] = issue_datetime_utc.astimezone(local_tz).isoformat() bond_token["token_status"] = _token.token_status bond_token["contract_version"] = _token.version @@ -451,13 +456,14 @@ async def retrieve_token(db: DBAsyncSession, token_address: str): InvalidParameterError, SendTransactionError, ContractRevertError, + OperationNotSupportedVersionError, ), ) async def update_token( db: DBAsyncSession, request: Request, token_address: str, - token: IbetStraightBondUpdate, + update_data: IbetStraightBondUpdate, issuer_address: str = Header(...), eoa_password: Optional[str] = Header(None), auth_token: Optional[str] = Header(None), @@ -505,12 +511,30 @@ async def update_token( if _token.token_status == 0: raise InvalidParameterError("this token is temporarily unavailable") + # Verify that the token version supports the operation + if _token.version < TokenVersion.V_23_12: + if ( + update_data.face_value_currency is not None + or update_data.interest_payment_currency is not None + or update_data.redemption_value_currency is not None + or update_data.base_fx_rate is not None + ): + raise OperationNotSupportedVersionError( + f"the operation is not supported in {_token.version}" + ) + + if _token.version < TokenVersion.V_24_06: + if update_data.require_personal_info_registered is not None: + raise OperationNotSupportedVersionError( + f"the operation is not supported in {_token.version}" + ) + # Send transaction try: token_contract = IbetStraightBondContract(token_address) original_contents = (await token_contract.get()).__dict__ await token_contract.update( - data=UpdateParams(**token.model_dump()), + data=UpdateParams(**update_data.model_dump()), tx_from=issuer_address, private_key=private_key, ) @@ -522,7 +546,7 @@ async def update_token( operation_log.token_address = token_address operation_log.issuer_address = issuer_address operation_log.type = TokenType.IBET_STRAIGHT_BOND.value - operation_log.arguments = token.model_dump(exclude_none=True) + operation_log.arguments = update_data.model_dump(exclude_none=True) operation_log.original_contents = original_contents operation_log.operation_category = TokenUpdateOperationCategory.UPDATE.value db.add(operation_log) @@ -563,14 +587,20 @@ async def list_bond_operation_log_history( ) ) if request_query.created_from: + _created_from = datetime.strptime( + request_query.created_from + ".000000", "%Y-%m-%d %H:%M:%S.%f" + ) stmt = stmt.where( TokenUpdateOperationLog.created - >= local_tz.localize(request_query.created_from).astimezone(utc_tz) + >= local_tz.localize(_created_from).astimezone(utc_tz) ) if request_query.created_to: + _created_to = datetime.strptime( + request_query.created_to + ".999999", "%Y-%m-%d %H:%M:%S.%f" + ) stmt = stmt.where( TokenUpdateOperationLog.created - <= local_tz.localize(request_query.created_to).astimezone(utc_tz) + <= local_tz.localize(_created_to).astimezone(utc_tz) ) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) @@ -680,7 +710,7 @@ async def list_additional_issuance_history( history = [] for _event in _events: - block_timestamp_utc = timezone("UTC").localize(_event.block_timestamp) + block_timestamp_utc = pytz.timezone("UTC").localize(_event.block_timestamp) history.append( { "transaction_hash": _event.transaction_hash, @@ -796,11 +826,6 @@ async def list_all_additional_issue_upload( get_query: ListAllAdditionalIssueUploadQuery = Depends(), issuer_address: Optional[str] = Header(None), ): - processed = get_query.processed - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Get a list of uploads stmt = select(BatchIssueRedeemUpload).where( and_( @@ -815,28 +840,28 @@ async def list_all_additional_issue_upload( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if processed is not None: - stmt = stmt.where(BatchIssueRedeemUpload.processed == processed) + if get_query.processed is not None: + stmt = stmt.where(BatchIssueRedeemUpload.processed == get_query.processed) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(BatchIssueRedeemUpload.created) else: # DESC stmt = stmt.order_by(desc(BatchIssueRedeemUpload.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _upload_list: Sequence[BatchIssueRedeemUpload] = (await db.scalars(stmt)).all() uploads = [] for _upload in _upload_list: - created_utc = timezone("UTC").localize(_upload.created) + created_utc = pytz.timezone("UTC").localize(_upload.created) uploads.append( { "batch_id": _upload.upload_id, @@ -851,8 +876,8 @@ async def list_all_additional_issue_upload( resp = { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "uploads": uploads, @@ -1042,11 +1067,6 @@ async def list_redeem_history( get_query: ListRedeemHistoryQuery = Depends(), ): """List redemption history""" - sort_item = get_query.sort_item - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Get token _token: Token | None = ( await db.scalars( @@ -1077,26 +1097,26 @@ async def list_redeem_history( count = total # Sort - sort_attr = getattr(IDXIssueRedeem, sort_item.value, None) - if sort_order == 0: # ASC + sort_attr = getattr(IDXIssueRedeem, get_query.sort_item.value, None) + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(sort_attr) else: # DESC stmt = stmt.order_by(desc(sort_attr)) - if sort_item != IDXIssueRedeemSortItem.BLOCK_TIMESTAMP: + if get_query.sort_item != IDXIssueRedeemSortItem.BLOCK_TIMESTAMP: # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(desc(IDXIssueRedeem.block_timestamp)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _events: Sequence[IDXIssueRedeem] = (await db.scalars(stmt)).all() history = [] for _event in _events: - block_timestamp_utc = timezone("UTC").localize(_event.block_timestamp) + block_timestamp_utc = pytz.timezone("UTC").localize(_event.block_timestamp) history.append( { "transaction_hash": _event.transaction_hash, @@ -1112,8 +1132,8 @@ async def list_redeem_history( { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "history": history, @@ -1212,11 +1232,6 @@ async def list_all_redeem_upload( get_query: ListAllRedeemUploadQuery = Depends(), issuer_address: Optional[str] = Header(None), ): - processed = get_query.processed - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Get a list of uploads stmt = select(BatchIssueRedeemUpload).where( and_( @@ -1232,28 +1247,28 @@ async def list_all_redeem_upload( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if processed is not None: - stmt = stmt.where(BatchIssueRedeemUpload.processed == processed) + if get_query.processed is not None: + stmt = stmt.where(BatchIssueRedeemUpload.processed == get_query.processed) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(BatchIssueRedeemUpload.created) else: # DESC stmt = stmt.order_by(desc(BatchIssueRedeemUpload.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _upload_list: Sequence[BatchIssueRedeemUpload] = (await db.scalars(stmt)).all() uploads = [] for _upload in _upload_list: - created_utc = timezone("UTC").localize(_upload.created) + created_utc = pytz.timezone("UTC").localize(_upload.created) uploads.append( { "batch_id": _upload.upload_id, @@ -1268,8 +1283,8 @@ async def list_all_redeem_upload( resp = { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "uploads": uploads, @@ -1489,10 +1504,10 @@ async def list_all_scheduled_events( token_events = [] for _token_event in _token_events: - scheduled_datetime_utc = timezone("UTC").localize( + scheduled_datetime_utc = pytz.timezone("UTC").localize( _token_event.scheduled_datetime ) - created_utc = timezone("UTC").localize(_token_event.created) + created_utc = pytz.timezone("UTC").localize(_token_event.created) token_events.append( { "scheduled_event_id": _token_event.event_id, @@ -1515,7 +1530,12 @@ async def list_all_scheduled_events( "/tokens/{token_address}/scheduled_events", response_model=ScheduledEventIdResponse, responses=get_routers_responses( - 422, 401, 404, AuthorizationError, InvalidParameterError + 422, + 401, + 404, + AuthorizationError, + InvalidParameterError, + OperationNotSupportedVersionError, ), ) async def schedule_new_update_event( @@ -1564,6 +1584,24 @@ async def schedule_new_update_event( if _token.token_status == 0: raise InvalidParameterError("this token is temporarily unavailable") + # Verify that the token version supports the operation + if _token.version < TokenVersion.V_23_12: + if ( + event_data.data.face_value_currency is not None + or event_data.data.interest_payment_currency is not None + or event_data.data.redemption_value_currency is not None + or event_data.data.base_fx_rate is not None + ): + raise OperationNotSupportedVersionError( + f"the operation is not supported in {_token.version}" + ) + + if _token.version < TokenVersion.V_24_06: + if event_data.data.require_personal_info_registered is not None: + raise OperationNotSupportedVersionError( + f"the operation is not supported in {_token.version}" + ) + # Register an event _scheduled_event = ScheduledEvents() _scheduled_event.event_id = str(uuid.uuid4()) @@ -1626,8 +1664,10 @@ async def retrieve_token_event( if _token_event is None: raise HTTPException(status_code=404, detail="event not found") - scheduled_datetime_utc = timezone("UTC").localize(_token_event.scheduled_datetime) - created_utc = timezone("UTC").localize(_token_event.created) + scheduled_datetime_utc = pytz.timezone("UTC").localize( + _token_event.scheduled_datetime + ) + created_utc = pytz.timezone("UTC").localize(_token_event.created) return json_response( { "scheduled_event_id": _token_event.event_id, @@ -1694,8 +1734,10 @@ async def delete_scheduled_event( if _token_event is None: raise HTTPException(status_code=404, detail="event not found") - scheduled_datetime_utc = timezone("UTC").localize(_token_event.scheduled_datetime) - created_utc = timezone("UTC").localize(_token_event.created) + scheduled_datetime_utc = pytz.timezone("UTC").localize( + _token_event.scheduled_datetime + ) + created_utc = pytz.timezone("UTC").localize(_token_event.created) rtn = { "scheduled_event_id": _token_event.event_id, "token_address": token_address, @@ -1726,21 +1768,6 @@ async def list_all_holders( issuer_address: str = Header(...), ): """List all bond token holders""" - include_former_holder = get_query.include_former_holder - balance = get_query.balance - balance_operator = get_query.balance_operator - pending_transfer = get_query.pending_transfer - pending_transfer_operator = get_query.pending_transfer_operator - locked = get_query.locked - locked_operator = get_query.locked_operator - account_address = get_query.account_address - holder_name = get_query.holder_name - key_manager = get_query.key_manager - sort_item = get_query.sort_item - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Validate Headers validate_headers(issuer_address=(issuer_address, address_is_valid_address)) @@ -1807,7 +1834,7 @@ async def list_all_holders( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if not include_former_holder: + if not get_query.include_former_holder: stmt = stmt.where( or_( IDXPosition.balance != 0, @@ -1818,75 +1845,109 @@ async def list_all_holders( ) ) - if balance is not None and balance_operator is not None: - match balance_operator: + if get_query.balance is not None and get_query.balance_operator is not None: + match get_query.balance_operator: case ValueOperator.EQUAL: - stmt = stmt.where(IDXPosition.balance == balance) + stmt = stmt.where(IDXPosition.balance == get_query.balance) case ValueOperator.GTE: - stmt = stmt.where(IDXPosition.balance >= balance) + stmt = stmt.where(IDXPosition.balance >= get_query.balance) + case ValueOperator.LTE: + stmt = stmt.where(IDXPosition.balance <= get_query.balance) + + if ( + get_query.pending_transfer is not None + and get_query.pending_transfer_operator is not None + ): + match get_query.pending_transfer_operator: + case ValueOperator.EQUAL: + stmt = stmt.where( + IDXPosition.pending_transfer == get_query.pending_transfer + ) + case ValueOperator.GTE: + stmt = stmt.where( + IDXPosition.pending_transfer >= get_query.pending_transfer + ) case ValueOperator.LTE: - stmt = stmt.where(IDXPosition.balance <= balance) + stmt = stmt.where( + IDXPosition.pending_transfer <= get_query.pending_transfer + ) - if pending_transfer is not None and pending_transfer_operator is not None: - match pending_transfer_operator: + if get_query.locked is not None and get_query.locked_operator is not None: + match get_query.locked_operator: case ValueOperator.EQUAL: - stmt = stmt.where(IDXPosition.pending_transfer == pending_transfer) + stmt = stmt.having(coalesce(locked_value, 0) == get_query.locked) case ValueOperator.GTE: - stmt = stmt.where(IDXPosition.pending_transfer >= pending_transfer) + stmt = stmt.having(coalesce(locked_value, 0) >= get_query.locked) case ValueOperator.LTE: - stmt = stmt.where(IDXPosition.pending_transfer <= pending_transfer) + stmt = stmt.having(coalesce(locked_value, 0) <= get_query.locked) - if locked is not None and locked_operator is not None: - match locked_operator: + if ( + get_query.balance_and_pending_transfer is not None + and get_query.balance_and_pending_transfer_operator is not None + ): + match get_query.balance_and_pending_transfer_operator: case ValueOperator.EQUAL: - stmt = stmt.having(locked_value == locked) + stmt = stmt.where( + IDXPosition.balance + IDXPosition.pending_transfer + == get_query.balance_and_pending_transfer + ) case ValueOperator.GTE: - stmt = stmt.having(locked_value >= locked) + stmt = stmt.where( + IDXPosition.balance + IDXPosition.pending_transfer + >= get_query.balance_and_pending_transfer + ) case ValueOperator.LTE: - stmt = stmt.having(locked_value <= locked) + stmt = stmt.where( + IDXPosition.balance + IDXPosition.pending_transfer + <= get_query.balance_and_pending_transfer + ) - if account_address is not None: - stmt = stmt.where(IDXPosition.account_address.like("%" + account_address + "%")) + if get_query.account_address is not None: + stmt = stmt.where( + IDXPosition.account_address.like("%" + get_query.account_address + "%") + ) - if holder_name is not None: + if get_query.holder_name is not None: stmt = stmt.where( IDXPersonalInfo._personal_info["name"] .as_string() - .like("%" + holder_name + "%") + .like("%" + get_query.holder_name + "%") ) - if key_manager is not None: + if get_query.key_manager is not None: stmt = stmt.where( IDXPersonalInfo._personal_info["key_manager"] .as_string() - .like("%" + key_manager + "%") + .like("%" + get_query.key_manager + "%") ) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_item == ListAllHoldersSortItem.holder_name: + if get_query.sort_item == ListAllHoldersSortItem.holder_name: sort_attr = IDXPersonalInfo._personal_info["name"].as_string() - elif sort_item == ListAllHoldersSortItem.key_manager: + elif get_query.sort_item == ListAllHoldersSortItem.key_manager: sort_attr = IDXPersonalInfo._personal_info["key_manager"].as_string() - elif sort_item == ListAllHoldersSortItem.locked: + elif get_query.sort_item == ListAllHoldersSortItem.locked: sort_attr = locked_value + elif get_query.sort_item == ListAllHoldersSortItem.balance_and_pending_transfer: + sort_attr = IDXPosition.balance + IDXPosition.pending_transfer else: - sort_attr = getattr(IDXPosition, sort_item) + sort_attr = getattr(IDXPosition, get_query.sort_item) - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(asc(sort_attr)) else: # DESC stmt = stmt.order_by(desc(sort_attr)) - if sort_item != ListAllHoldersSortItem.created: + if get_query.sort_item != ListAllHoldersSortItem.created: # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(asc(IDXPosition.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _holders: Sequence[ tuple[IDXPosition, int, IDXPersonalInfo | None, datetime | None] @@ -1939,8 +2000,8 @@ async def list_all_holders( "result_set": { "count": count, "total": total, - "limit": limit, - "offset": offset, + "limit": get_query.limit, + "offset": get_query.offset, }, "holders": holders, } @@ -2264,11 +2325,6 @@ async def list_all_personal_info_batch_registration_uploads( get_query: ListAllPersonalInfoBatchRegistrationUploadQuery = Depends(), ): """List all personal information batch registration uploads""" - status = get_query.status - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Verify that the token is issued by the issuer_address _token: Token | None = ( await db.scalars( @@ -2296,22 +2352,22 @@ async def list_all_personal_info_batch_registration_uploads( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if status is not None: - stmt = stmt.where(BatchRegisterPersonalInfoUpload.status == status) + if get_query.status is not None: + stmt = stmt.where(BatchRegisterPersonalInfoUpload.status == get_query.status) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(BatchRegisterPersonalInfoUpload.created) else: # DESC stmt = stmt.order_by(desc(BatchRegisterPersonalInfoUpload.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _upload_list: Sequence[BatchRegisterPersonalInfoUpload] = ( await db.scalars(stmt) @@ -2319,7 +2375,7 @@ async def list_all_personal_info_batch_registration_uploads( uploads = [] for _upload in _upload_list: - created_utc = timezone("UTC").localize(_upload.created) + created_utc = pytz.timezone("UTC").localize(_upload.created) uploads.append( { "batch_id": _upload.upload_id, @@ -2332,8 +2388,8 @@ async def list_all_personal_info_batch_registration_uploads( { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "uploads": uploads, @@ -2419,7 +2475,7 @@ async def batch_register_personal_info( { "batch_id": batch_id, "status": batch.status, - "created": timezone("UTC") + "created": pytz.timezone("UTC") .localize(batch.created) .astimezone(local_tz) .isoformat(), @@ -2644,7 +2700,7 @@ async def list_all_lock_events_by_bond( for lock_event in lock_events: token: Token = lock_event.Token bond_contract = await IbetStraightBondContract(token.token_address).get() - block_timestamp_utc = timezone("UTC").localize(lock_event.block_timestamp) + block_timestamp_utc = pytz.timezone("UTC").localize(lock_event.block_timestamp) resp_data.append( { "category": lock_event.category, @@ -2760,10 +2816,10 @@ async def transfer_ownership( async def list_transfer_history( db: DBAsyncSession, token_address: str, - request_query: ListTransferHistoryQuery = Depends(), + query: ListTransferHistoryQuery = Depends(), ): """List token transfer history""" - # Get token + # Check if the token has been issued _token: Token | None = ( await db.scalars( select(Token) @@ -2804,33 +2860,71 @@ async def list_transfer_history( ) .where(IDXTransfer.token_address == token_address) ) - total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if request_query.source_event is not None: - stmt = stmt.where(IDXTransfer.source_event == request_query.source_event) - if request_query.data is not None: + # Filter + if query.block_timestamp_from is not None: stmt = stmt.where( - cast(IDXTransfer.data, String).like("%" + request_query.data + "%") + IDXTransfer.block_timestamp + >= local_tz.localize(query.block_timestamp_from).astimezone(UTC) ) - + if query.block_timestamp_to is not None: + stmt = stmt.where( + IDXTransfer.block_timestamp + <= local_tz.localize(query.block_timestamp_to).astimezone(UTC) + ) + if query.from_address is not None: + stmt = stmt.where(IDXTransfer.from_address == query.from_address) + if query.to_address is not None: + stmt = stmt.where(IDXTransfer.to_address == query.to_address) + if query.from_address_name: + stmt = stmt.where( + from_address_personal_info._personal_info["name"] + .as_string() + .like("%" + query.from_address_name + "%") + ) + if query.to_address_name: + stmt = stmt.where( + to_address_personal_info._personal_info["name"] + .as_string() + .like("%" + query.to_address_name + "%") + ) + if query.amount is not None and query.amount_operator is not None: + match query.amount_operator: + case ValueOperator.EQUAL: + stmt = stmt.where(IDXTransfer.amount == query.amount) + case ValueOperator.GTE: + stmt = stmt.where(IDXTransfer.amount >= query.amount) + case ValueOperator.LTE: + stmt = stmt.where(IDXTransfer.amount <= query.amount) + if query.source_event is not None: + stmt = stmt.where(IDXTransfer.source_event == query.source_event) + if query.data is not None: + stmt = stmt.where(cast(IDXTransfer.data, String).like("%" + query.data + "%")) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - sort_attr = getattr(IDXTransfer, request_query.sort_item.value, None) - if request_query.sort_order == 0: # ASC + match query.sort_item: + case ListTransferHistorySortItem.FROM_ADDRESS_NAME: + sort_attr = from_address_personal_info._personal_info["name"].as_string() + case ListTransferHistorySortItem.TO_ADDRESS_NAME: + sort_attr = to_address_personal_info._personal_info["name"].as_string() + case _: + sort_attr = getattr(IDXTransfer, query.sort_item.value, None) + + if query.sort_order == 0: # ASC stmt = stmt.order_by(sort_attr) else: # DESC stmt = stmt.order_by(desc(sort_attr)) - if request_query.sort_item != ListTransferHistorySortItem.BLOCK_TIMESTAMP: + if query.sort_item != ListTransferHistorySortItem.BLOCK_TIMESTAMP: # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(desc(IDXTransfer.block_timestamp)) # Pagination - if request_query.limit is not None: - stmt = stmt.limit(request_query.limit) - if request_query.offset is not None: - stmt = stmt.offset(request_query.offset) + if query.limit is not None: + stmt = stmt.limit(query.limit) + if query.offset is not None: + stmt = stmt.offset(query.offset) _transfers: Sequence[ tuple[IDXTransfer, IDXPersonalInfo | None, IDXPersonalInfo | None] @@ -2838,7 +2932,7 @@ async def list_transfer_history( transfer_history = [] for _transfer, _from_address_personal_info, _to_address_personal_info in _transfers: - block_timestamp_utc = timezone("UTC").localize(_transfer.block_timestamp) + block_timestamp_utc = pytz.timezone("UTC").localize(_transfer.block_timestamp) transfer_history.append( { "transaction_hash": _transfer.transaction_hash, @@ -2866,8 +2960,8 @@ async def list_transfer_history( { "result_set": { "count": count, - "offset": request_query.offset, - "limit": request_query.limit, + "offset": query.offset, + "limit": query.limit, "total": total, }, "transfer_history": transfer_history, @@ -3024,14 +3118,6 @@ async def list_token_transfer_approval_history( get_query: ListTransferApprovalHistoryQuery = Depends(), ): """List token transfer approval history""" - from_address = get_query.from_address - to_address = get_query.to_address - status = get_query.status - sort_item = get_query.sort_item - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Get token _token: Token | None = ( await db.scalars( @@ -3148,33 +3234,33 @@ async def list_token_transfer_approval_history( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Search Filter - if from_address is not None: - stmt = stmt.where(subquery.from_address == from_address) - if to_address is not None: - stmt = stmt.where(subquery.to_address == to_address) - if status is not None: - stmt = stmt.where(literal_column("status").in_(status)) + if get_query.from_address is not None: + stmt = stmt.where(subquery.from_address == get_query.from_address) + if get_query.to_address is not None: + stmt = stmt.where(subquery.to_address == get_query.to_address) + if get_query.status is not None: + stmt = stmt.where(literal_column("status").in_(get_query.status)) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_item != IDXTransferApprovalsSortItem.STATUS: - sort_attr = getattr(subquery, sort_item, None) + if get_query.sort_item != IDXTransferApprovalsSortItem.STATUS: + sort_attr = getattr(subquery, get_query.sort_item, None) else: sort_attr = literal_column("status") - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(sort_attr) else: # DESC stmt = stmt.order_by(desc(sort_attr)) - if sort_item != IDXTransferApprovalsSortItem.ID: + if get_query.sort_item != IDXTransferApprovalsSortItem.ID: # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(desc(subquery.id)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _transfer_approvals: Sequence[ tuple[ @@ -3216,12 +3302,12 @@ async def list_token_transfer_approval_history( else: issuer_cancelable = True - application_datetime_utc = timezone("UTC").localize( + application_datetime_utc = pytz.timezone("UTC").localize( _transfer_approval.application_datetime ) application_datetime = application_datetime_utc.astimezone(local_tz).isoformat() - application_blocktimestamp_utc = timezone("UTC").localize( + application_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.application_blocktimestamp ) application_blocktimestamp = application_blocktimestamp_utc.astimezone( @@ -3229,7 +3315,7 @@ async def list_token_transfer_approval_history( ).isoformat() if _transfer_approval.approval_datetime is not None: - approval_datetime_utc = timezone("UTC").localize( + approval_datetime_utc = pytz.timezone("UTC").localize( _transfer_approval.approval_datetime ) approval_datetime = approval_datetime_utc.astimezone(local_tz).isoformat() @@ -3237,7 +3323,7 @@ async def list_token_transfer_approval_history( approval_datetime = None if _transfer_approval.approval_blocktimestamp is not None: - approval_blocktimestamp_utc = timezone("UTC").localize( + approval_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.approval_blocktimestamp ) approval_blocktimestamp = approval_blocktimestamp_utc.astimezone( @@ -3247,7 +3333,7 @@ async def list_token_transfer_approval_history( approval_blocktimestamp = None if _transfer_approval.cancellation_blocktimestamp is not None: - cancellation_blocktimestamp_utc = timezone("UTC").localize( + cancellation_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.cancellation_blocktimestamp ) cancellation_blocktimestamp = cancellation_blocktimestamp_utc.astimezone( @@ -3302,8 +3388,8 @@ async def list_token_transfer_approval_history( { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "transfer_approval_history": transfer_approval_history, @@ -3470,7 +3556,7 @@ async def update_transfer_approval( # a cancelTransfer is performed immediately. # - CANCEL -> cancelTransfer try: - now = str(datetime.utcnow().timestamp()) + now = str(datetime.now(UTC).replace(tzinfo=None).timestamp()) if data.operation_type == UpdateTransferApprovalOperationType.APPROVE: if _transfer_approval.exchange_address == config.ZERO_ADDRESS: _data = { @@ -3657,12 +3743,12 @@ async def retrieve_transfer_approval_history( else: issuer_cancelable = True - application_datetime_utc = timezone("UTC").localize( + application_datetime_utc = pytz.timezone("UTC").localize( _transfer_approval.application_datetime ) application_datetime = application_datetime_utc.astimezone(local_tz).isoformat() - application_blocktimestamp_utc = timezone("UTC").localize( + application_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.application_blocktimestamp ) application_blocktimestamp = application_blocktimestamp_utc.astimezone( @@ -3670,7 +3756,7 @@ async def retrieve_transfer_approval_history( ).isoformat() if _transfer_approval.approval_datetime is not None: - approval_datetime_utc = timezone("UTC").localize( + approval_datetime_utc = pytz.timezone("UTC").localize( _transfer_approval.approval_datetime ) approval_datetime = approval_datetime_utc.astimezone(local_tz).isoformat() @@ -3678,7 +3764,7 @@ async def retrieve_transfer_approval_history( approval_datetime = None if _transfer_approval.approval_blocktimestamp is not None: - approval_blocktimestamp_utc = timezone("UTC").localize( + approval_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.approval_blocktimestamp ) approval_blocktimestamp = approval_blocktimestamp_utc.astimezone( @@ -3688,7 +3774,7 @@ async def retrieve_transfer_approval_history( approval_blocktimestamp = None if _transfer_approval.cancellation_blocktimestamp is not None: - cancellation_blocktimestamp_utc = timezone("UTC").localize( + cancellation_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.cancellation_blocktimestamp ) cancellation_blocktimestamp = cancellation_blocktimestamp_utc.astimezone( @@ -3923,7 +4009,7 @@ async def list_bulk_transfer_upload( uploads = [] for _upload in _uploads: - created_utc = timezone("UTC").localize(_upload.created) + created_utc = pytz.timezone("UTC").localize(_upload.created) uploads.append( { "issuer_address": _upload.issuer_address, diff --git a/app/routers/e2e_messaging.py b/app/routers/e2e_messaging.py index d73fa287..697265fd 100644 --- a/app/routers/e2e_messaging.py +++ b/app/routers/e2e_messaging.py @@ -20,7 +20,7 @@ import json import re import secrets -from datetime import datetime +from datetime import UTC, datetime from typing import List, Optional, Sequence import boto3 @@ -163,7 +163,9 @@ async def create_account( _account_rsa_key.rsa_private_key = rsa_private_key _account_rsa_key.rsa_public_key = rsa_public_key _account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(rsa_passphrase) - _account_rsa_key.block_timestamp = datetime.utcfromtimestamp(block["timestamp"]) + _account_rsa_key.block_timestamp = datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) db.add(_account_rsa_key) # Insert initial transaction execution management record diff --git a/app/routers/ledger.py b/app/routers/ledger.py index c20fbbfc..465867ac 100644 --- a/app/routers/ledger.py +++ b/app/routers/ledger.py @@ -30,6 +30,7 @@ from app.database import DBAsyncSession from app.exceptions import Integer64bitLimitExceededError, InvalidParameterError from app.model.blockchain import ( + ContractPersonalInfoType, IbetShareContract, IbetStraightBondContract, PersonalInfoContract, @@ -249,18 +250,30 @@ async def retrieve_ledger_history( _details.token_detail_type for _details in _ibet_fin_details_list ] # Update PersonalInfo + some_personal_info_not_registered = False for details in resp["details"]: if details["token_detail_type"] in _ibet_fin_token_detail_type_list: for data in details["data"]: - personal_info = await __get_personal_info( + personal_info, _pi_not_registered = await __get_personal_info( token_address=token_address, token_type=_token.type, account_address=data["account_address"], db=db, ) - if personal_info is not None: - data["name"] = personal_info.get("name", None) - data["address"] = personal_info.get("address", None) + data["name"] = personal_info.get("name", None) + data["address"] = personal_info.get("address", None) + if _pi_not_registered: + some_personal_info_not_registered = True + details["some_personal_info_not_registered"] = ( + some_personal_info_not_registered + ) + else: + # NOTE: Implementation for backward compatibility + # In specifications prior to v24.6, the item "some_personal_info_not_registered" does not exist, + # so data for which the item does not exist is overwritten with False. + for details in resp["details"]: + if details.get("some_personal_info_not_registered") is None: + details["some_personal_info_not_registered"] = False return json_response(resp) @@ -878,52 +891,88 @@ async def delete_ledger_details_data( async def __get_personal_info( token_address: str, token_type: str, account_address: str, db: AsyncSession -): +) -> tuple[dict, bool]: + # NOTE: + # For tokens with require_personal_info_registered = False, search only indexed data. + # If indexed data does not exist, return the default value. + token: Token | None = ( await db.scalars( select(Token).where(Token.token_address == token_address).limit(1) ) ).first() if token is None: - return None - else: - _idx_personal_info: IDXPersonalInfo | None = ( - await db.scalars( - select(IDXPersonalInfo) - .where( - and_( - IDXPersonalInfo.account_address == account_address, - IDXPersonalInfo.issuer_address == token.issuer_address, - ) + personal_info_not_registered = True + return ContractPersonalInfoType().model_dump(), personal_info_not_registered + + # Issuer cannot have any personal info + if account_address == token.issuer_address: + personal_info_not_registered = False + return ( + ContractPersonalInfoType( + key_manager=None, + name=None, + address=None, + postal_code=None, + email=None, + birth=None, + ).model_dump(), + personal_info_not_registered, + ) + + # Search indexed data + _idx_personal_info: IDXPersonalInfo | None = ( + await db.scalars( + select(IDXPersonalInfo) + .where( + and_( + IDXPersonalInfo.account_address == account_address, + IDXPersonalInfo.issuer_address == token.issuer_address, ) + ) + .limit(1) + ) + ).first() + if ( + _idx_personal_info is not None + and any(_idx_personal_info.personal_info.values()) is not False + ): + # Get personal info from DB + personal_info_not_registered = False + return _idx_personal_info.personal_info, personal_info_not_registered + + # Get token attributes + token_contract = None + if token_type == TokenType.IBET_SHARE.value: + token_contract = await IbetShareContract(token_address).get() + elif token_type == TokenType.IBET_STRAIGHT_BOND.value: + token_contract = await IbetStraightBondContract(token_address).get() + + if token_contract.require_personal_info_registered is True: + # Get issuer account + issuer_account = ( + await db.scalars( + select(Account) + .where(Account.issuer_address == token.issuer_address) .limit(1) ) ).first() - if _idx_personal_info is not None: - # Get personal info from DB - personal_info = _idx_personal_info.personal_info + + # Retrieve personal info from contract storage + personal_info_contract = PersonalInfoContract( + issuer=issuer_account, + contract_address=token_contract.personal_info_contract_address, + ) + personal_info = await personal_info_contract.get_info( + account_address=account_address, default_value=None + ) + if any(personal_info.values()) is False: + personal_info_not_registered = True else: - # Get issuer account - issuer_account = ( - await db.scalars( - select(Account) - .where(Account.issuer_address == token.issuer_address) - .limit(1) - ) - ).first() - - # Get personal info from contract - token_contract = None - if token_type == TokenType.IBET_SHARE.value: - token_contract = await IbetShareContract(token_address).get() - elif token_type == TokenType.IBET_STRAIGHT_BOND.value: - token_contract = await IbetStraightBondContract(token_address).get() - - personal_info_contract = PersonalInfoContract( - issuer=issuer_account, - contract_address=token_contract.personal_info_contract_address, - ) - personal_info = await personal_info_contract.get_info( - account_address=account_address, default_value=None - ) - return personal_info + personal_info_not_registered = False + else: + # Do not retrieve contract data and return the default value + personal_info = ContractPersonalInfoType().model_dump() + personal_info_not_registered = True + + return personal_info, personal_info_not_registered diff --git a/app/routers/settlement.py b/app/routers/settlement.py new file mode 100644 index 00000000..590b298e --- /dev/null +++ b/app/routers/settlement.py @@ -0,0 +1,695 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import re +import secrets +from datetime import UTC +from typing import Optional, Sequence + +import boto3 +import eth_keyfile +import pytz +from coincurve import PublicKey +from eth_keyfile import decode_keyfile_json +from eth_utils import keccak, to_checksum_address +from fastapi import APIRouter, Depends, Header, HTTPException, Path, Request +from sqlalchemy import and_, desc, func, select + +import config +from app.database import DBAsyncSession +from app.exceptions import InvalidParameterError, SendTransactionError +from app.model.blockchain.exchange import IbetSecurityTokenDVP +from app.model.blockchain.tx_params.ibet_security_token_dvp import ( + AbortDeliveryParams, + CancelDeliveryParams, + CreateDeliveryParams, + FinishDeliveryParams, +) +from app.model.db import DVPAgentAccount, IDXDelivery, TransactionLock +from app.model.schema import ( + AbortDVPDeliveryRequest, + CancelDVPDeliveryRequest, + CreateDVPAgentAccountRequest, + CreateDVPDeliveryRequest, + DVPAgentAccountChangeEOAPasswordRequest, + DVPAgentAccountResponse, + FinishDVPDeliveryRequest, + ListAllDVPAgentAccountResponse, + ListAllDVPDeliveriesQuery, + ListAllDVPDeliveriesResponse, + RetrieveDVPDeliveryResponse, +) +from app.utils.check_utils import ( + address_is_valid_address, + check_auth, + eoa_password_is_encrypted_value, + validate_headers, +) +from app.utils.docs_utils import get_routers_responses +from app.utils.e2ee_utils import E2EEUtils +from app.utils.fastapi_utils import json_response +from config import ( + AWS_KMS_GENERATE_RANDOM_ENABLED, + AWS_REGION_NAME, + E2EE_REQUEST_ENABLED, + EOA_PASSWORD_PATTERN, + EOA_PASSWORD_PATTERN_MSG, +) + +router = APIRouter(prefix="/settlement", tags=["token_common"]) + +local_tz = pytz.timezone(config.TZ) + + +# POST: /settlement/dvp/agent/accounts +@router.post( + "/dvp/agent/accounts", + operation_id="CreateDVPAgentAccount", + response_model=DVPAgentAccountResponse, + responses=get_routers_responses(422, InvalidParameterError), +) +async def create_account( + db: DBAsyncSession, + create_req: CreateDVPAgentAccountRequest, +): + """Create DVP-Payment Agent Account""" + + # Check Password Policy(EOA password) + eoa_password = ( + E2EEUtils.decrypt(create_req.eoa_password) + if E2EE_REQUEST_ENABLED + else create_req.eoa_password + ) + if not re.match(EOA_PASSWORD_PATTERN, eoa_password): + raise InvalidParameterError(EOA_PASSWORD_PATTERN_MSG) + + # Generate Ethereum Key + if AWS_KMS_GENERATE_RANDOM_ENABLED: + kms = boto3.client(service_name="kms", region_name=AWS_REGION_NAME) + result = kms.generate_random(NumberOfBytes=32) + private_key = keccak(result.get("Plaintext")) + else: + private_key = keccak(secrets.token_bytes(32)) + public_key = PublicKey.from_valid_secret(private_key).format(compressed=False)[1:] + addr = to_checksum_address(keccak(public_key)[-20:]) + keyfile_json = eth_keyfile.create_keyfile_json( + private_key=private_key, password=eoa_password.encode("utf-8"), kdf="pbkdf2" + ) + + # Register account data to the DB + _account = DVPAgentAccount() + _account.account_address = addr + _account.keyfile = keyfile_json + _account.eoa_password = E2EEUtils.encrypt(eoa_password) + _account.is_deleted = False + db.add(_account) + + # Insert initial transaction execution management record + _tm = TransactionLock() + _tm.tx_from = addr + db.add(_tm) + + await db.commit() + + return json_response( + { + "account_address": _account.account_address, + "is_deleted": _account.is_deleted, + } + ) + + +# GET: /settlement/dvp/agent/accounts +@router.get( + "/dvp/agent/accounts", + operation_id="ListAllDVPAgentAccount", + response_model=ListAllDVPAgentAccountResponse, +) +async def list_all_accounts(db: DBAsyncSession): + """List all DVP-Payment Agent accounts""" + + _accounts: Sequence[DVPAgentAccount] = ( + await db.scalars(select(DVPAgentAccount)) + ).all() + + account_list = [ + { + "account_address": _account.account_address, + "is_deleted": _account.is_deleted, + } + for _account in _accounts + ] + return json_response(account_list) + + +# DELETE: /settlement/dvp/agent/accounts/{account_address} +@router.delete( + "/dvp/agent/account/{account_address}", + operation_id="DeleteDVPAgentAccount", + response_model=DVPAgentAccountResponse, + responses=get_routers_responses(404), +) +async def delete_account(db: DBAsyncSession, account_address: str): + """Logically delete an DVP-Payment Agent Account""" + + # Search for an account + _account: DVPAgentAccount | None = ( + await db.scalars( + select(DVPAgentAccount) + .where(DVPAgentAccount.account_address == account_address) + .limit(1) + ) + ).first() + if _account is None: + raise HTTPException(status_code=404, detail="account is not exists") + + # Update account + _account.is_deleted = True + await db.merge(_account) + await db.commit() + + return json_response( + { + "account_address": _account.account_address, + "is_deleted": _account.is_deleted, + } + ) + + +# POST: /settlement/dvp/agent/accounts/{account_address}/eoa_password +@router.post( + "/dvp/agent/account/{account_address}/eoa_password", + operation_id="ChangeDVPAgentAccountPassword", + response_model=None, + responses=get_routers_responses(404, 422, InvalidParameterError), +) +async def change_eoa_password( + db: DBAsyncSession, + account_address: str, + change_req: DVPAgentAccountChangeEOAPasswordRequest, +): + """Change Account's EOA Password""" + + # Search for an account + _account: DVPAgentAccount | None = ( + await db.scalars( + select(DVPAgentAccount) + .where(DVPAgentAccount.account_address == account_address) + .limit(1) + ) + ).first() + if _account is None: + raise HTTPException(status_code=404, detail="account is not exists") + + # Check Old Password + old_eoa_password = ( + E2EEUtils.decrypt(change_req.old_eoa_password) + if E2EE_REQUEST_ENABLED + else change_req.old_eoa_password + ) + correct_eoa_password = E2EEUtils.decrypt(_account.eoa_password) + if old_eoa_password != correct_eoa_password: + raise InvalidParameterError("old password mismatch") + + # Check Password Policy + eoa_password = ( + E2EEUtils.decrypt(change_req.eoa_password) + if E2EE_REQUEST_ENABLED + else change_req.eoa_password + ) + if not re.match(EOA_PASSWORD_PATTERN, eoa_password): + raise InvalidParameterError(EOA_PASSWORD_PATTERN_MSG) + + # Get Ethereum Key + private_key = eth_keyfile.decode_keyfile_json( + raw_keyfile_json=_account.keyfile, password=old_eoa_password.encode("utf-8") + ) + + # Create New Ethereum Key File + keyfile_json = eth_keyfile.create_keyfile_json( + private_key=private_key, password=eoa_password.encode("utf-8"), kdf="pbkdf2" + ) + + # Update data + _account.keyfile = keyfile_json + _account.eoa_password = E2EEUtils.encrypt(eoa_password) + await db.merge(_account) + + await db.commit() + + return + + +# GET: /settlement/dvp/{exchange_address}/deliveries +@router.get( + "/dvp/{exchange_address}/deliveries", + operation_id="ListAllDVPDeliveries", + response_model=ListAllDVPDeliveriesResponse, + responses=get_routers_responses(404, 422, InvalidParameterError), +) +async def list_all_dvp_deliveries( + db: DBAsyncSession, + exchange_address: str, + request_query: ListAllDVPDeliveriesQuery = Depends(), +): + """List of DVP delivery""" + stmt = select(IDXDelivery).where( + IDXDelivery.exchange_address == exchange_address, + ) + total = await db.scalar(select(func.count()).select_from(stmt.subquery())) + + if request_query.token_address is not None: + stmt = stmt.where(IDXDelivery.token_address == request_query.token_address) + if request_query.seller_address is not None: + stmt = stmt.where(IDXDelivery.seller_address == request_query.seller_address) + if request_query.agent_address is not None: + stmt = stmt.where(IDXDelivery.agent_address == request_query.agent_address) + if request_query.valid is not None: + stmt = stmt.where(IDXDelivery.valid == request_query.valid) + if request_query.status is not None: + stmt = stmt.where(IDXDelivery.status == request_query.status) + if request_query.create_blocktimestamp_from is not None: + stmt = stmt.where( + IDXDelivery.create_blocktimestamp + >= local_tz.localize(request_query.create_blocktimestamp_from).astimezone( + tz=UTC + ) + ) + if request_query.create_blocktimestamp_to is not None: + stmt = stmt.where( + IDXDelivery.create_blocktimestamp + <= local_tz.localize(request_query.create_blocktimestamp_to).astimezone( + tz=UTC + ) + ) + + count = await db.scalar(select(func.count()).select_from(stmt.subquery())) + + # Sort + if request_query.sort_order == 0: # ASC + stmt = stmt.order_by(IDXDelivery.create_blocktimestamp) + else: # DESC + stmt = stmt.order_by(desc(IDXDelivery.create_blocktimestamp)) + + # Pagination + if request_query.limit is not None: + stmt = stmt.limit(request_query.limit) + if request_query.offset is not None: + stmt = stmt.offset(request_query.offset) + + _deliveries: Sequence[IDXDelivery] = (await db.scalars(stmt)).all() + + deliveries = [] + for _delivery in _deliveries: + if _delivery.create_blocktimestamp is not None: + create_blocktimestamp = ( + local_tz.localize(_delivery.create_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + create_blocktimestamp = None + if _delivery.cancel_blocktimestamp is not None: + cancel_blocktimestamp = ( + local_tz.localize(_delivery.cancel_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + cancel_blocktimestamp = None + if _delivery.confirm_blocktimestamp is not None: + confirm_blocktimestamp = ( + local_tz.localize(_delivery.confirm_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + confirm_blocktimestamp = None + if _delivery.finish_blocktimestamp is not None: + finish_blocktimestamp = ( + local_tz.localize(_delivery.finish_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + finish_blocktimestamp = None + if _delivery.abort_blocktimestamp is not None: + abort_blocktimestamp = ( + local_tz.localize(_delivery.abort_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + abort_blocktimestamp = None + + deliveries.append( + { + "exchange_address": _delivery.exchange_address, + "delivery_id": _delivery.delivery_id, + "token_address": _delivery.token_address, + "buyer_address": _delivery.buyer_address, + "seller_address": _delivery.seller_address, + "amount": _delivery.amount, + "agent_address": _delivery.agent_address, + "data": _delivery.data, + "create_blocktimestamp": create_blocktimestamp, + "create_transaction_hash": _delivery.create_transaction_hash, + "cancel_blocktimestamp": cancel_blocktimestamp, + "cancel_transaction_hash": _delivery.cancel_transaction_hash, + "confirm_blocktimestamp": confirm_blocktimestamp, + "confirm_transaction_hash": _delivery.confirm_transaction_hash, + "finish_blocktimestamp": finish_blocktimestamp, + "finish_transaction_hash": _delivery.finish_transaction_hash, + "abort_blocktimestamp": abort_blocktimestamp, + "abort_transaction_hash": _delivery.abort_transaction_hash, + "confirmed": _delivery.confirmed, + "valid": _delivery.valid, + "status": _delivery.status, + } + ) + + return json_response( + { + "result_set": { + "count": count, + "offset": request_query.offset, + "limit": request_query.limit, + "total": total, + }, + "deliveries": deliveries, + } + ) + + +# POST: /settlement/dvp/{exchange_address}/deliveries +@router.post( + "/dvp/{exchange_address}/deliveries", + operation_id="CreateDVPDelivery", + response_model=None, + responses=get_routers_responses( + 404, 422, InvalidParameterError, SendTransactionError + ), +) +async def create_dvp_delivery( + db: DBAsyncSession, + req: Request, + exchange_address: str, + data: CreateDVPDeliveryRequest, + issuer_address: str = Header(...), + eoa_password: Optional[str] = Header(None), + auth_token: Optional[str] = Header(None), +): + # Validate Headers + validate_headers( + issuer_address=(issuer_address, address_is_valid_address), + eoa_password=(eoa_password, eoa_password_is_encrypted_value), + ) + + # Authentication + _account, decrypt_password = await check_auth( + request=req, + db=db, + issuer_address=issuer_address, + eoa_password=eoa_password, + auth_token=auth_token, + ) + + # Get private key + keyfile_json = _account.keyfile + private_key = decode_keyfile_json( + raw_keyfile_json=keyfile_json, password=decrypt_password.encode("utf-8") + ) + + # Create delivery + dvp_contract = IbetSecurityTokenDVP(contract_address=exchange_address) + try: + _data = { + "token_address": data.token_address, + "buyer_address": data.buyer_address, + "amount": data.amount, + "agent_address": data.agent_address, + "data": data.data, + } + await dvp_contract.create_delivery( + data=CreateDeliveryParams(**_data), + tx_from=issuer_address, + private_key=private_key, + ) + except SendTransactionError: + raise SendTransactionError("failed to create delivery") + + return + + +# GET: /settlement/dvp/{exchange_address}/delivery/{delivery_id} +@router.get( + "/dvp/{exchange_address}/delivery/{delivery_id}", + operation_id="RetrieveDVPDelivery", + response_model=RetrieveDVPDeliveryResponse, + responses=get_routers_responses(404, 422, InvalidParameterError), +) +async def retrieve_dvp_delivery( + db: DBAsyncSession, + exchange_address: str = Path(..., description="Exchange Address"), + delivery_id: int = Path(..., description="Delivery Id"), +): + """Retrieve a dvp delivery""" + _delivery: IDXDelivery | None = ( + await db.scalars( + select(IDXDelivery) + .where( + and_( + IDXDelivery.exchange_address == exchange_address, + IDXDelivery.delivery_id == delivery_id, + ) + ) + .limit(1) + ) + ).first() + if _delivery is None: + raise HTTPException(status_code=404, detail="delivery not found") + + if _delivery.create_blocktimestamp is not None: + create_blocktimestamp = ( + local_tz.localize(_delivery.create_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + create_blocktimestamp = None + if _delivery.cancel_blocktimestamp is not None: + cancel_blocktimestamp = ( + local_tz.localize(_delivery.cancel_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + cancel_blocktimestamp = None + if _delivery.confirm_blocktimestamp is not None: + confirm_blocktimestamp = ( + local_tz.localize(_delivery.confirm_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + confirm_blocktimestamp = None + if _delivery.finish_blocktimestamp is not None: + finish_blocktimestamp = ( + local_tz.localize(_delivery.finish_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + finish_blocktimestamp = None + if _delivery.abort_blocktimestamp is not None: + abort_blocktimestamp = ( + local_tz.localize(_delivery.abort_blocktimestamp) + .astimezone(tz=UTC) + .isoformat() + ) + else: + abort_blocktimestamp = None + + return json_response( + { + "exchange_address": _delivery.exchange_address, + "delivery_id": _delivery.delivery_id, + "token_address": _delivery.token_address, + "buyer_address": _delivery.buyer_address, + "seller_address": _delivery.seller_address, + "amount": _delivery.amount, + "agent_address": _delivery.agent_address, + "data": _delivery.data, + "create_blocktimestamp": create_blocktimestamp, + "create_transaction_hash": _delivery.create_transaction_hash, + "cancel_blocktimestamp": cancel_blocktimestamp, + "cancel_transaction_hash": _delivery.cancel_transaction_hash, + "confirm_blocktimestamp": confirm_blocktimestamp, + "confirm_transaction_hash": _delivery.confirm_transaction_hash, + "finish_blocktimestamp": finish_blocktimestamp, + "finish_transaction_hash": _delivery.finish_transaction_hash, + "abort_blocktimestamp": abort_blocktimestamp, + "abort_transaction_hash": _delivery.abort_transaction_hash, + "confirmed": _delivery.confirmed, + "valid": _delivery.valid, + "status": _delivery.status, + } + ) + + +# POST: /settlement/dvp/{exchange_address}/delivery/{delivery_id} +@router.post( + "/dvp/{exchange_address}/delivery/{delivery_id}", + operation_id="UpdateDVPDelivery", + response_model=None, + responses=get_routers_responses( + 404, 422, InvalidParameterError, SendTransactionError + ), +) +async def update_dvp_delivery( + db: DBAsyncSession, + req: Request, + exchange_address: str, + delivery_id: str, + data: CancelDVPDeliveryRequest | FinishDVPDeliveryRequest | AbortDVPDeliveryRequest, + issuer_address: Optional[str] = Header(None), + eoa_password: Optional[str] = Header(None), + auth_token: Optional[str] = Header(None), +): + match data.operation_type: + case "Cancel": + # Validate Headers + validate_headers( + issuer_address=(issuer_address, address_is_valid_address), + eoa_password=(eoa_password, eoa_password_is_encrypted_value), + ) + + # Authentication + _account, decrypt_password = await check_auth( + request=req, + db=db, + issuer_address=issuer_address, + eoa_password=eoa_password, + auth_token=auth_token, + ) + + # Get private key + keyfile_json = _account.keyfile + private_key = decode_keyfile_json( + raw_keyfile_json=keyfile_json, password=decrypt_password.encode("utf-8") + ) + + # Cancel delivery + dvp_contract = IbetSecurityTokenDVP(contract_address=exchange_address) + try: + _data = {"delivery_id": delivery_id} + await dvp_contract.cancel_delivery( + data=CancelDeliveryParams(**_data), + tx_from=issuer_address, + private_key=private_key, + ) + except SendTransactionError: + raise SendTransactionError("failed to cancel delivery") + return + + case "Finish": + # Search for agent account + agent_account: DVPAgentAccount | None = ( + await db.scalars( + select(DVPAgentAccount) + .where(DVPAgentAccount.account_address == data.account_address) + .limit(1) + ) + ).first() + if agent_account is None: + raise HTTPException( + status_code=404, detail="agent account is not exists" + ) + + # Authentication + eoa_password = ( + E2EEUtils.decrypt(data.eoa_password) + if E2EE_REQUEST_ENABLED + else data.eoa_password + ) + correct_eoa_pass = E2EEUtils.decrypt(agent_account.eoa_password) + if eoa_password != correct_eoa_pass: + raise InvalidParameterError("password mismatch") + + # Get private key + keyfile_json = agent_account.keyfile + private_key = decode_keyfile_json( + raw_keyfile_json=keyfile_json, password=correct_eoa_pass.encode("utf-8") + ) + + # Cancel delivery + dvp_contract = IbetSecurityTokenDVP(contract_address=exchange_address) + try: + _data = {"delivery_id": delivery_id} + await dvp_contract.finish_delivery( + data=FinishDeliveryParams(**_data), + tx_from=agent_account.account_address, + private_key=private_key, + ) + except SendTransactionError: + raise SendTransactionError("failed to finish delivery") + return + + case "Abort": + # Search for agent account + agent_account: DVPAgentAccount | None = ( + await db.scalars( + select(DVPAgentAccount) + .where(DVPAgentAccount.account_address == data.account_address) + .limit(1) + ) + ).first() + if agent_account is None: + raise HTTPException( + status_code=404, detail="agent account is not exists" + ) + + # Authentication + eoa_password = ( + E2EEUtils.decrypt(data.eoa_password) + if E2EE_REQUEST_ENABLED + else data.eoa_password + ) + correct_eoa_pass = E2EEUtils.decrypt(agent_account.eoa_password) + if eoa_password != correct_eoa_pass: + raise InvalidParameterError("password mismatch") + + # Get private key + keyfile_json = agent_account.keyfile + private_key = decode_keyfile_json( + raw_keyfile_json=keyfile_json, password=correct_eoa_pass.encode("utf-8") + ) + + # Cancel delivery + dvp_contract = IbetSecurityTokenDVP(contract_address=exchange_address) + try: + _data = {"delivery_id": delivery_id} + await dvp_contract.abort_delivery( + data=AbortDeliveryParams(**_data), + tx_from=agent_account.account_address, + private_key=private_key, + ) + except SendTransactionError: + raise SendTransactionError("failed to abort delivery") + return diff --git a/app/routers/share.py b/app/routers/share.py index b3bcc35b..c3229614 100644 --- a/app/routers/share.py +++ b/app/routers/share.py @@ -18,14 +18,14 @@ """ import uuid -from datetime import datetime +from datetime import UTC, datetime from decimal import Decimal from typing import List, Optional, Sequence +import pytz from eth_keyfile import decode_keyfile_json from fastapi import APIRouter, Depends, Header, Query, Request from fastapi.exceptions import HTTPException -from pytz import timezone from sqlalchemy import ( String, and_, @@ -42,6 +42,7 @@ select, ) from sqlalchemy.orm import aliased +from sqlalchemy.sql.functions import coalesce import config from app import log @@ -51,6 +52,7 @@ ContractRevertError, InvalidParameterError, OperationNotAllowedStateError, + OperationNotSupportedVersionError, SendTransactionError, ) from app.model.blockchain import ( @@ -169,8 +171,8 @@ ) LOG = log.get_logger() -local_tz = timezone(config.TZ) -utc_tz = timezone("UTC") +local_tz = pytz.timezone(config.TZ) +utc_tz = pytz.timezone("UTC") # POST: /share/tokens @@ -246,6 +248,7 @@ async def issue_token( update_items = [ "tradable_exchange_contract_address", "personal_info_contract_address", + "require_personal_info_registered", "transferable", "status", "is_offering", @@ -304,7 +307,9 @@ async def issue_token( _utxo.token_address = contract_address _utxo.amount = token.total_supply _utxo.block_number = block["number"] - _utxo.block_timestamp = datetime.utcfromtimestamp(block["timestamp"]) + _utxo.block_timestamp = datetime.fromtimestamp(block["timestamp"], UTC).replace( + tzinfo=None + ) db.add(_utxo) token_status = 1 # succeeded @@ -317,7 +322,7 @@ async def issue_token( _token.token_address = contract_address _token.abi = abi _token.token_status = token_status - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # Register operation log @@ -372,7 +377,7 @@ async def list_all_tokens( for token in tokens: # Get contract data share_token = (await IbetShareContract(token.token_address).get()).__dict__ - issue_datetime_utc = timezone("UTC").localize(token.created) + issue_datetime_utc = pytz.timezone("UTC").localize(token.created) share_token["issue_datetime"] = issue_datetime_utc.astimezone( local_tz ).isoformat() @@ -413,7 +418,7 @@ async def retrieve_token(db: DBAsyncSession, token_address: str): # Get contract data share_token = (await IbetShareContract(token_address).get()).__dict__ - issue_datetime_utc = timezone("UTC").localize(_token.created) + issue_datetime_utc = pytz.timezone("UTC").localize(_token.created) share_token["issue_datetime"] = issue_datetime_utc.astimezone(local_tz).isoformat() share_token["token_status"] = _token.token_status share_token["contract_version"] = _token.version @@ -434,13 +439,14 @@ async def retrieve_token(db: DBAsyncSession, token_address: str): InvalidParameterError, SendTransactionError, ContractRevertError, + OperationNotSupportedVersionError, ), ) async def update_token( db: DBAsyncSession, request: Request, token_address: str, - token: IbetShareUpdate, + update_data: IbetShareUpdate, issuer_address: str = Header(...), eoa_password: Optional[str] = Header(None), auth_token: Optional[str] = Header(None), @@ -488,12 +494,19 @@ async def update_token( if _token.token_status == 0: raise InvalidParameterError("this token is temporarily unavailable") + # Verify that the token version supports the operation + if _token.version < TokenVersion.V_24_06: + if update_data.require_personal_info_registered is not None: + raise OperationNotSupportedVersionError( + f"the operation is not supported in {_token.version}" + ) + # Send transaction try: token_contract = IbetShareContract(token_address) original_contents = (await token_contract.get()).__dict__ await token_contract.update( - data=UpdateParams(**token.model_dump()), + data=UpdateParams(**update_data.model_dump()), tx_from=issuer_address, private_key=private_key, ) @@ -505,7 +518,7 @@ async def update_token( operation_log.token_address = token_address operation_log.issuer_address = issuer_address operation_log.type = TokenType.IBET_SHARE.value - operation_log.arguments = token.model_dump(exclude_none=True) + operation_log.arguments = update_data.model_dump(exclude_none=True) operation_log.original_contents = original_contents operation_log.operation_category = TokenUpdateOperationCategory.UPDATE.value db.add(operation_log) @@ -546,14 +559,20 @@ async def list_share_operation_log_history( ) ) if request_query.created_from: + _created_from = datetime.strptime( + request_query.created_from + ".000000", "%Y-%m-%d %H:%M:%S.%f" + ) stmt = stmt.where( TokenUpdateOperationLog.created - >= local_tz.localize(request_query.created_from).astimezone(utc_tz) + >= local_tz.localize(_created_from).astimezone(utc_tz) ) if request_query.created_to: + _created_to = datetime.strptime( + request_query.created_to + ".999999", "%Y-%m-%d %H:%M:%S.%f" + ) stmt = stmt.where( TokenUpdateOperationLog.created - <= local_tz.localize(request_query.created_to).astimezone(utc_tz) + <= local_tz.localize(_created_to).astimezone(utc_tz) ) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) @@ -663,7 +682,7 @@ async def list_additional_issuance_history( history = [] for _event in _events: - block_timestamp_utc = timezone("UTC").localize(_event.block_timestamp) + block_timestamp_utc = pytz.timezone("UTC").localize(_event.block_timestamp) history.append( { "transaction_hash": _event.transaction_hash, @@ -779,11 +798,6 @@ async def list_all_additional_issue_upload( get_query: ListAllAdditionalIssueUploadQuery = Depends(), issuer_address: Optional[str] = Header(None), ): - processed = get_query.processed - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Get a list of uploads stmt = select(BatchIssueRedeemUpload).where( and_( @@ -798,28 +812,28 @@ async def list_all_additional_issue_upload( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if processed is not None: - stmt = stmt.where(BatchIssueRedeemUpload.processed == processed) + if get_query.processed is not None: + stmt = stmt.where(BatchIssueRedeemUpload.processed == get_query.processed) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(BatchIssueRedeemUpload.created) else: # DESC stmt = stmt.order_by(desc(BatchIssueRedeemUpload.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _upload_list: Sequence[BatchIssueRedeemUpload] = (await db.scalars(stmt)).all() uploads = [] for _upload in _upload_list: - created_utc = timezone("UTC").localize(_upload.created) + created_utc = pytz.timezone("UTC").localize(_upload.created) uploads.append( { "batch_id": _upload.upload_id, @@ -834,8 +848,8 @@ async def list_all_additional_issue_upload( resp = { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "uploads": uploads, @@ -1025,11 +1039,6 @@ async def list_redeem_history( get_query: ListRedeemHistoryQuery = Depends(), ): """List redemption history""" - sort_item = get_query.sort_item - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Get token _token: Token | None = ( await db.scalars( @@ -1060,26 +1069,26 @@ async def list_redeem_history( count = total # Sort - sort_attr = getattr(IDXIssueRedeem, sort_item.value, None) - if sort_order == 0: # ASC + sort_attr = getattr(IDXIssueRedeem, get_query.sort_item.value, None) + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(sort_attr) else: # DESC stmt = stmt.order_by(desc(sort_attr)) - if sort_item != IDXIssueRedeemSortItem.BLOCK_TIMESTAMP: + if get_query.sort_item != IDXIssueRedeemSortItem.BLOCK_TIMESTAMP: # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(desc(IDXIssueRedeem.block_timestamp)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _events: Sequence[IDXIssueRedeem] = (await db.scalars(stmt)).all() history = [] for _event in _events: - block_timestamp_utc = timezone("UTC").localize(_event.block_timestamp) + block_timestamp_utc = pytz.timezone("UTC").localize(_event.block_timestamp) history.append( { "transaction_hash": _event.transaction_hash, @@ -1095,8 +1104,8 @@ async def list_redeem_history( { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "history": history, @@ -1195,10 +1204,6 @@ async def list_all_redeem_upload( get_query: ListAllRedeemUploadQuery = Depends(), issuer_address: Optional[str] = Header(None), ): - processed = get_query.processed - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit # Get a list of uploads stmt = select(BatchIssueRedeemUpload).where( @@ -1215,28 +1220,28 @@ async def list_all_redeem_upload( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if processed is not None: - stmt = stmt.where(BatchIssueRedeemUpload.processed == processed) + if get_query.processed is not None: + stmt = stmt.where(BatchIssueRedeemUpload.processed == get_query.processed) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(BatchIssueRedeemUpload.created) else: # DESC stmt = stmt.order_by(desc(BatchIssueRedeemUpload.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _upload_list: Sequence[BatchIssueRedeemUpload] = (await db.scalars(stmt)).all() uploads = [] for _upload in _upload_list: - created_utc = timezone("UTC").localize(_upload.created) + created_utc = pytz.timezone("UTC").localize(_upload.created) uploads.append( { "batch_id": _upload.upload_id, @@ -1251,8 +1256,8 @@ async def list_all_redeem_upload( resp = { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "uploads": uploads, @@ -1472,10 +1477,10 @@ async def list_all_scheduled_events( token_events = [] for _token_event in _token_events: - scheduled_datetime_utc = timezone("UTC").localize( + scheduled_datetime_utc = pytz.timezone("UTC").localize( _token_event.scheduled_datetime ) - created_utc = timezone("UTC").localize(_token_event.created) + created_utc = pytz.timezone("UTC").localize(_token_event.created) token_events.append( { "scheduled_event_id": _token_event.event_id, @@ -1498,7 +1503,12 @@ async def list_all_scheduled_events( "/tokens/{token_address}/scheduled_events", response_model=ScheduledEventIdResponse, responses=get_routers_responses( - 422, 401, 404, AuthorizationError, InvalidParameterError + 422, + 401, + 404, + AuthorizationError, + InvalidParameterError, + OperationNotSupportedVersionError, ), ) async def schedule_new_update_event( @@ -1547,6 +1557,13 @@ async def schedule_new_update_event( if _token.token_status == 0: raise InvalidParameterError("this token is temporarily unavailable") + # Verify that the token version supports the operation + if _token.version < TokenVersion.V_24_06: + if event_data.data.require_personal_info_registered is not None: + raise OperationNotSupportedVersionError( + f"the operation is not supported in {_token.version}" + ) + # Register an event _scheduled_event = ScheduledEvents() _scheduled_event.event_id = str(uuid.uuid4()) @@ -1610,8 +1627,10 @@ async def retrieve_token_event( if _token_event is None: raise HTTPException(status_code=404, detail="event not found") - scheduled_datetime_utc = timezone("UTC").localize(_token_event.scheduled_datetime) - created_utc = timezone("UTC").localize(_token_event.created) + scheduled_datetime_utc = pytz.timezone("UTC").localize( + _token_event.scheduled_datetime + ) + created_utc = pytz.timezone("UTC").localize(_token_event.created) return json_response( { "scheduled_event_id": _token_event.event_id, @@ -1678,8 +1697,10 @@ async def delete_scheduled_event( if _token_event is None: raise HTTPException(status_code=404, detail="event not found") - scheduled_datetime_utc = timezone("UTC").localize(_token_event.scheduled_datetime) - created_utc = timezone("UTC").localize(_token_event.created) + scheduled_datetime_utc = pytz.timezone("UTC").localize( + _token_event.scheduled_datetime + ) + created_utc = pytz.timezone("UTC").localize(_token_event.created) rtn = { "scheduled_event_id": _token_event.event_id, "token_address": token_address, @@ -1710,21 +1731,6 @@ async def list_all_holders( issuer_address: str = Header(...), ): """List all share token holders""" - include_former_holder = get_query.include_former_holder - balance = get_query.balance - balance_operator = get_query.balance_operator - pending_transfer = get_query.pending_transfer - pending_transfer_operator = get_query.pending_transfer_operator - locked = get_query.locked - locked_operator = get_query.locked_operator - account_address = get_query.account_address - holder_name = get_query.holder_name - key_manager = get_query.key_manager - sort_item = get_query.sort_item - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Validate Headers validate_headers(issuer_address=(issuer_address, address_is_valid_address)) @@ -1791,7 +1797,7 @@ async def list_all_holders( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if not include_former_holder: + if not get_query.include_former_holder: stmt = stmt.where( or_( IDXPosition.balance != 0, @@ -1802,75 +1808,109 @@ async def list_all_holders( ) ) - if balance is not None and balance_operator is not None: - match balance_operator: + if get_query.balance is not None and get_query.balance_operator is not None: + match get_query.balance_operator: case ValueOperator.EQUAL: - stmt = stmt.where(IDXPosition.balance == balance) + stmt = stmt.where(IDXPosition.balance == get_query.balance) case ValueOperator.GTE: - stmt = stmt.where(IDXPosition.balance >= balance) + stmt = stmt.where(IDXPosition.balance >= get_query.balance) case ValueOperator.LTE: - stmt = stmt.where(IDXPosition.balance <= balance) + stmt = stmt.where(IDXPosition.balance <= get_query.balance) - if pending_transfer is not None and pending_transfer_operator is not None: - match pending_transfer_operator: + if ( + get_query.pending_transfer is not None + and get_query.pending_transfer_operator is not None + ): + match get_query.pending_transfer_operator: case ValueOperator.EQUAL: - stmt = stmt.where(IDXPosition.pending_transfer == pending_transfer) + stmt = stmt.where( + IDXPosition.pending_transfer == get_query.pending_transfer + ) case ValueOperator.GTE: - stmt = stmt.where(IDXPosition.pending_transfer >= pending_transfer) + stmt = stmt.where( + IDXPosition.pending_transfer >= get_query.pending_transfer + ) case ValueOperator.LTE: - stmt = stmt.where(IDXPosition.pending_transfer <= pending_transfer) + stmt = stmt.where( + IDXPosition.pending_transfer <= get_query.pending_transfer + ) - if locked is not None and locked_operator is not None: - match locked_operator: + if get_query.locked is not None and get_query.locked_operator is not None: + match get_query.locked_operator: case ValueOperator.EQUAL: - stmt = stmt.having(locked_value == locked) + stmt = stmt.having(coalesce(locked_value, 0) == get_query.locked) case ValueOperator.GTE: - stmt = stmt.having(locked_value >= locked) + stmt = stmt.having(coalesce(locked_value, 0) >= get_query.locked) case ValueOperator.LTE: - stmt = stmt.having(locked_value <= locked) + stmt = stmt.having(coalesce(locked_value, 0) <= get_query.locked) - if account_address is not None: - stmt = stmt.where(IDXPosition.account_address.like("%" + account_address + "%")) + if ( + get_query.balance_and_pending_transfer is not None + and get_query.balance_and_pending_transfer_operator is not None + ): + match get_query.balance_and_pending_transfer_operator: + case ValueOperator.EQUAL: + stmt = stmt.where( + IDXPosition.balance + IDXPosition.pending_transfer + == get_query.balance_and_pending_transfer + ) + case ValueOperator.GTE: + stmt = stmt.where( + IDXPosition.balance + IDXPosition.pending_transfer + >= get_query.balance_and_pending_transfer + ) + case ValueOperator.LTE: + stmt = stmt.where( + IDXPosition.balance + IDXPosition.pending_transfer + <= get_query.balance_and_pending_transfer + ) - if holder_name is not None: + if get_query.account_address is not None: + stmt = stmt.where( + IDXPosition.account_address.like("%" + get_query.account_address + "%") + ) + + if get_query.holder_name is not None: stmt = stmt.where( IDXPersonalInfo._personal_info["name"] .as_string() - .like("%" + holder_name + "%") + .like("%" + get_query.holder_name + "%") ) - if key_manager is not None: + if get_query.key_manager is not None: stmt = stmt.where( IDXPersonalInfo._personal_info["key_manager"] .as_string() - .like("%" + key_manager + "%") + .like("%" + get_query.key_manager + "%") ) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_item == ListAllHoldersSortItem.holder_name: + if get_query.sort_item == ListAllHoldersSortItem.holder_name: sort_attr = IDXPersonalInfo._personal_info["name"].as_string() - elif sort_item == ListAllHoldersSortItem.key_manager: + elif get_query.sort_item == ListAllHoldersSortItem.key_manager: sort_attr = IDXPersonalInfo._personal_info["key_manager"].as_string() - elif sort_item == ListAllHoldersSortItem.locked: + elif get_query.sort_item == ListAllHoldersSortItem.locked: sort_attr = locked_value + elif get_query.sort_item == ListAllHoldersSortItem.balance_and_pending_transfer: + sort_attr = IDXPosition.balance + IDXPosition.pending_transfer else: - sort_attr = getattr(IDXPosition, sort_item) + sort_attr = getattr(IDXPosition, get_query.sort_item) - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(asc(sort_attr)) else: # DESC stmt = stmt.order_by(desc(sort_attr)) - if sort_item != ListAllHoldersSortItem.created: + if get_query.sort_item != ListAllHoldersSortItem.created: # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(asc(IDXPosition.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _holders: Sequence[ tuple[IDXPosition, int, IDXPersonalInfo | None, datetime | None] @@ -1923,8 +1963,8 @@ async def list_all_holders( "result_set": { "count": count, "total": total, - "limit": limit, - "offset": offset, + "limit": get_query.limit, + "offset": get_query.offset, }, "holders": holders, } @@ -2248,11 +2288,6 @@ async def list_all_personal_info_batch_registration_uploads( get_query: ListAllPersonalInfoBatchRegistrationUploadQuery = Depends(), ): """List all personal information batch registration uploads""" - status = get_query.status - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Verify that the token is issued by the issuer_address _token: Token | None = ( await db.scalars( @@ -2280,22 +2315,22 @@ async def list_all_personal_info_batch_registration_uploads( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if status is not None: - stmt = stmt.where(BatchRegisterPersonalInfoUpload.status == status) + if get_query.status is not None: + stmt = stmt.where(BatchRegisterPersonalInfoUpload.status == get_query.status) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(BatchRegisterPersonalInfoUpload.created) else: # DESC stmt = stmt.order_by(desc(BatchRegisterPersonalInfoUpload.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _upload_list: Sequence[BatchRegisterPersonalInfoUpload] = ( await db.scalars(stmt) @@ -2303,7 +2338,7 @@ async def list_all_personal_info_batch_registration_uploads( uploads = [] for _upload in _upload_list: - created_utc = timezone("UTC").localize(_upload.created) + created_utc = pytz.timezone("UTC").localize(_upload.created) uploads.append( { "batch_id": _upload.upload_id, @@ -2316,8 +2351,8 @@ async def list_all_personal_info_batch_registration_uploads( { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "uploads": uploads, @@ -2403,7 +2438,7 @@ async def batch_register_personal_info( { "batch_id": batch_id, "status": batch.status, - "created": timezone("UTC") + "created": pytz.timezone("UTC") .localize(batch.created) .astimezone(local_tz) .isoformat(), @@ -2631,7 +2666,7 @@ async def list_all_lock_events_by_share( for lock_event in lock_events: token: Token = lock_event.Token share_contract = await IbetShareContract(token.token_address).get() - block_timestamp_utc = timezone("UTC").localize(lock_event.block_timestamp) + block_timestamp_utc = pytz.timezone("UTC").localize(lock_event.block_timestamp) resp_data.append( { "category": lock_event.category, @@ -2748,10 +2783,10 @@ async def transfer_ownership( async def list_transfer_history( db: DBAsyncSession, token_address: str, - request_query: ListTransferHistoryQuery = Depends(), + query: ListTransferHistoryQuery = Depends(), ): """List token transfer history""" - # Get token + # Check if the token has been issued _token: Token | None = ( await db.scalars( select(Token) @@ -2792,33 +2827,71 @@ async def list_transfer_history( ) .where(IDXTransfer.token_address == token_address) ) - total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - if request_query.source_event is not None: - stmt = stmt.where(IDXTransfer.source_event == request_query.source_event) - if request_query.data is not None: + # Filter + if query.block_timestamp_from is not None: stmt = stmt.where( - cast(IDXTransfer.data, String).like("%" + request_query.data + "%") + IDXTransfer.block_timestamp + >= local_tz.localize(query.block_timestamp_from).astimezone(UTC) ) - + if query.block_timestamp_to is not None: + stmt = stmt.where( + IDXTransfer.block_timestamp + <= local_tz.localize(query.block_timestamp_to).astimezone(UTC) + ) + if query.from_address is not None: + stmt = stmt.where(IDXTransfer.from_address == query.from_address) + if query.to_address is not None: + stmt = stmt.where(IDXTransfer.to_address == query.to_address) + if query.from_address_name: + stmt = stmt.where( + from_address_personal_info._personal_info["name"] + .as_string() + .like("%" + query.from_address_name + "%") + ) + if query.to_address_name: + stmt = stmt.where( + to_address_personal_info._personal_info["name"] + .as_string() + .like("%" + query.to_address_name + "%") + ) + if query.amount is not None and query.amount_operator is not None: + match query.amount_operator: + case ValueOperator.EQUAL: + stmt = stmt.where(IDXTransfer.amount == query.amount) + case ValueOperator.GTE: + stmt = stmt.where(IDXTransfer.amount >= query.amount) + case ValueOperator.LTE: + stmt = stmt.where(IDXTransfer.amount <= query.amount) + if query.source_event is not None: + stmt = stmt.where(IDXTransfer.source_event == query.source_event) + if query.data is not None: + stmt = stmt.where(cast(IDXTransfer.data, String).like("%" + query.data + "%")) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - sort_attr = getattr(IDXTransfer, request_query.sort_item.value, None) - if request_query.sort_order == 0: # ASC + match query.sort_item: + case ListTransferHistorySortItem.FROM_ADDRESS_NAME: + sort_attr = from_address_personal_info._personal_info["name"].as_string() + case ListTransferHistorySortItem.TO_ADDRESS_NAME: + sort_attr = to_address_personal_info._personal_info["name"].as_string() + case _: + sort_attr = getattr(IDXTransfer, query.sort_item.value, None) + + if query.sort_order == 0: # ASC stmt = stmt.order_by(sort_attr) else: # DESC stmt = stmt.order_by(desc(sort_attr)) - if request_query.sort_item != ListTransferHistorySortItem.BLOCK_TIMESTAMP: + if query.sort_item != ListTransferHistorySortItem.BLOCK_TIMESTAMP: # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(desc(IDXTransfer.block_timestamp)) # Pagination - if request_query.limit is not None: - stmt = stmt.limit(request_query.limit) - if request_query.offset is not None: - stmt = stmt.offset(request_query.offset) + if query.limit is not None: + stmt = stmt.limit(query.limit) + if query.offset is not None: + stmt = stmt.offset(query.offset) _transfers: Sequence[ tuple[IDXTransfer, IDXPersonalInfo | None, IDXPersonalInfo | None] @@ -2826,7 +2899,7 @@ async def list_transfer_history( transfer_history = [] for _transfer, _from_address_personal_info, _to_address_personal_info in _transfers: - block_timestamp_utc = timezone("UTC").localize(_transfer.block_timestamp) + block_timestamp_utc = pytz.timezone("UTC").localize(_transfer.block_timestamp) transfer_history.append( { "transaction_hash": _transfer.transaction_hash, @@ -2854,8 +2927,8 @@ async def list_transfer_history( { "result_set": { "count": count, - "offset": request_query.offset, - "limit": request_query.limit, + "offset": query.offset, + "limit": query.limit, "total": total, }, "transfer_history": transfer_history, @@ -3010,14 +3083,6 @@ async def list_token_transfer_approval_history( get_query: ListTransferApprovalHistoryQuery = Depends(), ): """List token transfer approval history""" - from_address = get_query.from_address - to_address = get_query.to_address - status = get_query.status - sort_item = get_query.sort_item - sort_order = get_query.sort_order - offset = get_query.offset - limit = get_query.limit - # Get token _token: Token | None = ( await db.scalars( @@ -3134,33 +3199,33 @@ async def list_token_transfer_approval_history( total = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Search Filter - if from_address is not None: - stmt = stmt.where(subquery.from_address == from_address) - if to_address is not None: - stmt = stmt.where(subquery.to_address == to_address) - if status is not None: - stmt = stmt.where(literal_column("status").in_(status)) + if get_query.from_address is not None: + stmt = stmt.where(subquery.from_address == get_query.from_address) + if get_query.to_address is not None: + stmt = stmt.where(subquery.to_address == get_query.to_address) + if get_query.status is not None: + stmt = stmt.where(literal_column("status").in_(get_query.status)) count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_item != IDXTransferApprovalsSortItem.STATUS: - sort_attr = getattr(subquery, sort_item, None) + if get_query.sort_item != IDXTransferApprovalsSortItem.STATUS: + sort_attr = getattr(subquery, get_query.sort_item, None) else: sort_attr = literal_column("status") - if sort_order == 0: # ASC + if get_query.sort_order == 0: # ASC stmt = stmt.order_by(sort_attr) else: # DESC stmt = stmt.order_by(desc(sort_attr)) - if sort_item != IDXTransferApprovalsSortItem.ID: + if get_query.sort_item != IDXTransferApprovalsSortItem.ID: # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(desc(subquery.id)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) _transfer_approvals: Sequence[ tuple[ @@ -3202,12 +3267,12 @@ async def list_token_transfer_approval_history( else: issuer_cancelable = True - application_datetime_utc = timezone("UTC").localize( + application_datetime_utc = pytz.timezone("UTC").localize( _transfer_approval.application_datetime ) application_datetime = application_datetime_utc.astimezone(local_tz).isoformat() - application_blocktimestamp_utc = timezone("UTC").localize( + application_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.application_blocktimestamp ) application_blocktimestamp = application_blocktimestamp_utc.astimezone( @@ -3215,7 +3280,7 @@ async def list_token_transfer_approval_history( ).isoformat() if _transfer_approval.approval_datetime is not None: - approval_datetime_utc = timezone("UTC").localize( + approval_datetime_utc = pytz.timezone("UTC").localize( _transfer_approval.approval_datetime ) approval_datetime = approval_datetime_utc.astimezone(local_tz).isoformat() @@ -3223,7 +3288,7 @@ async def list_token_transfer_approval_history( approval_datetime = None if _transfer_approval.approval_blocktimestamp is not None: - approval_blocktimestamp_utc = timezone("UTC").localize( + approval_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.approval_blocktimestamp ) approval_blocktimestamp = approval_blocktimestamp_utc.astimezone( @@ -3233,7 +3298,7 @@ async def list_token_transfer_approval_history( approval_blocktimestamp = None if _transfer_approval.cancellation_blocktimestamp is not None: - cancellation_blocktimestamp_utc = timezone("UTC").localize( + cancellation_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.cancellation_blocktimestamp ) cancellation_blocktimestamp = cancellation_blocktimestamp_utc.astimezone( @@ -3288,8 +3353,8 @@ async def list_token_transfer_approval_history( { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "transfer_approval_history": transfer_approval_history, @@ -3456,7 +3521,7 @@ async def update_transfer_approval( # a cancelTransfer is performed immediately. # - CANCEL -> cancelTransfer try: - now = str(datetime.utcnow().timestamp()) + now = str(datetime.now(UTC).replace(tzinfo=None).timestamp()) if data.operation_type == UpdateTransferApprovalOperationType.APPROVE: if _transfer_approval.exchange_address == config.ZERO_ADDRESS: _data = { @@ -3643,12 +3708,12 @@ async def retrieve_transfer_approval_history( else: issuer_cancelable = True - application_datetime_utc = timezone("UTC").localize( + application_datetime_utc = pytz.timezone("UTC").localize( _transfer_approval.application_datetime ) application_datetime = application_datetime_utc.astimezone(local_tz).isoformat() - application_blocktimestamp_utc = timezone("UTC").localize( + application_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.application_blocktimestamp ) application_blocktimestamp = application_blocktimestamp_utc.astimezone( @@ -3656,7 +3721,7 @@ async def retrieve_transfer_approval_history( ).isoformat() if _transfer_approval.approval_datetime is not None: - approval_datetime_utc = timezone("UTC").localize( + approval_datetime_utc = pytz.timezone("UTC").localize( _transfer_approval.approval_datetime ) approval_datetime = approval_datetime_utc.astimezone(local_tz).isoformat() @@ -3664,7 +3729,7 @@ async def retrieve_transfer_approval_history( approval_datetime = None if _transfer_approval.approval_blocktimestamp is not None: - approval_blocktimestamp_utc = timezone("UTC").localize( + approval_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.approval_blocktimestamp ) approval_blocktimestamp = approval_blocktimestamp_utc.astimezone( @@ -3674,7 +3739,7 @@ async def retrieve_transfer_approval_history( approval_blocktimestamp = None if _transfer_approval.cancellation_blocktimestamp is not None: - cancellation_blocktimestamp_utc = timezone("UTC").localize( + cancellation_blocktimestamp_utc = pytz.timezone("UTC").localize( _transfer_approval.cancellation_blocktimestamp ) cancellation_blocktimestamp = cancellation_blocktimestamp_utc.astimezone( @@ -3910,7 +3975,7 @@ async def list_bulk_transfer_upload( uploads = [] for _upload in _uploads: - created_utc = timezone("UTC").localize(_upload.created) + created_utc = pytz.timezone("UTC").localize(_upload.created) uploads.append( { "issuer_address": _upload.issuer_address, diff --git a/app/routers/token_holders.py b/app/routers/token_holders.py index 58a73e6e..6359a612 100644 --- a/app/routers/token_holders.py +++ b/app/routers/token_holders.py @@ -18,16 +18,20 @@ """ import uuid +from datetime import datetime from typing import Optional, Sequence +import pytz from fastapi import APIRouter, Depends, Header, Path, Query from fastapi.exceptions import HTTPException from sqlalchemy import and_, asc, desc, func, select +import config from app.database import DBAsyncSession from app.exceptions import InvalidParameterError from app.model.db import ( IDXPersonalInfo, + IDXPersonalInfoHistory, Token, TokenHolder, TokenHolderBatchStatus, @@ -37,6 +41,8 @@ CreateTokenHoldersListRequest, CreateTokenHoldersListResponse, ListAllTokenHolderCollectionsResponse, + ListTokenHoldersPersonalInfoHistoryQuery, + ListTokenHoldersPersonalInfoHistoryResponse, ListTokenHoldersPersonalInfoQuery, ListTokenHoldersPersonalInfoResponse, RetrieveTokenHoldersListResponse, @@ -52,45 +58,85 @@ prefix="/token", tags=["token_common"], ) +local_tz = pytz.timezone(config.TZ) +utc_tz = pytz.timezone("UTC") # GET: /token/holders/personal_info @router.get( "/holders/personal_info", + operation_id="ListTokenHoldersPersonalInfo", response_model=ListTokenHoldersPersonalInfoResponse, responses=get_routers_responses(422), ) async def list_all_token_holders_personal_info( db: DBAsyncSession, issuer_address: str = Header(...), - request_query: ListTokenHoldersPersonalInfoQuery = Depends(), + get_query: ListTokenHoldersPersonalInfoQuery = Depends(), ): """Lists the personal information of all registered holders linked to the token issuer""" + # Validate Headers validate_headers(issuer_address=(issuer_address, address_is_valid_address)) - offset = request_query.offset - limit = request_query.limit - sort_order = request_query.sort_order # default: asc - + # Get all data stmt = select(IDXPersonalInfo).where( IDXPersonalInfo.issuer_address == issuer_address ) total = await db.scalar(select(func.count()).select_from(stmt.subquery())) + # Filter + if get_query.account_address: + stmt = stmt.where(IDXPersonalInfo.account_address == get_query.account_address) + if get_query.created_from: + _created_from = datetime.strptime( + get_query.created_from + ".000000", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfo.created + >= local_tz.localize(_created_from).astimezone(utc_tz) + ) + if get_query.created_to: + _created_to = datetime.strptime( + get_query.created_to + ".999999", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfo.created <= local_tz.localize(_created_to).astimezone(utc_tz) + ) + if get_query.modified_from: + _modified_from = datetime.strptime( + get_query.modified_from + ".000000", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfo.modified + >= local_tz.localize(_modified_from).astimezone(utc_tz) + ) + if get_query.modified_to: + _modified_to = datetime.strptime( + get_query.modified_to + ".999999", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfo.modified + <= local_tz.localize(_modified_to).astimezone(utc_tz) + ) + count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if sort_order == 0: + sort_attr = getattr(IDXPersonalInfo, get_query.sort_item, None) + if get_query.sort_order == 0: # ASC + stmt = stmt.order_by(sort_attr) + else: # DESC + stmt = stmt.order_by(desc(sort_attr)) + if get_query.sort_item != IDXPersonalInfo.created: + # NOTE: Set secondary sort for consistent results stmt = stmt.order_by(IDXPersonalInfo.created) - else: - stmt = stmt.order_by(desc(IDXPersonalInfo.created)) # Pagination - if limit is not None: - stmt = stmt.limit(limit) - if offset is not None: - stmt = stmt.offset(offset) + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) personal_info_list: Sequence[IDXPersonalInfo] = (await db.scalars(stmt)).all() data = [_personal_info.json() for _personal_info in personal_info_list] @@ -99,8 +145,101 @@ async def list_all_token_holders_personal_info( { "result_set": { "count": count, - "offset": offset, - "limit": limit, + "offset": get_query.offset, + "limit": get_query.limit, + "total": total, + }, + "personal_info": data, + }, + ) + + +# GET: /token/holders/personal_info/history +@router.get( + "/holders/personal_info/history", + operation_id="ListTokenHoldersPersonalInfoHistory", + response_model=ListTokenHoldersPersonalInfoHistoryResponse, + responses=get_routers_responses(422), +) +async def list_all_token_holders_personal_info_history( + db: DBAsyncSession, + issuer_address: str = Header(...), + get_query: ListTokenHoldersPersonalInfoHistoryQuery = Depends(), +): + """List personal information historical data""" + + # Validate Headers + validate_headers(issuer_address=(issuer_address, address_is_valid_address)) + + # Get all data + stmt = select(IDXPersonalInfoHistory).where( + IDXPersonalInfoHistory.issuer_address == issuer_address + ) + total = await db.scalar(select(func.count()).select_from(stmt.subquery())) + + # Filter + if get_query.account_address is not None: + stmt = stmt.where( + IDXPersonalInfoHistory.account_address == get_query.account_address + ) + if get_query.event_type is not None: + stmt = stmt.where(IDXPersonalInfoHistory.event_type == get_query.event_type) + if get_query.block_timestamp_from: + _block_timestamp_from = datetime.strptime( + get_query.block_timestamp_from + ".000000", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfoHistory.block_timestamp + >= local_tz.localize(_block_timestamp_from).astimezone(utc_tz) + ) + if get_query.block_timestamp_to: + _block_timestamp_to = datetime.strptime( + get_query.block_timestamp_to + ".999999", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfoHistory.block_timestamp + <= local_tz.localize(_block_timestamp_to).astimezone(utc_tz) + ) + if get_query.created_from: + _created_from = datetime.strptime( + get_query.created_from + ".000000", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfoHistory.created + >= local_tz.localize(_created_from).astimezone(utc_tz) + ) + if get_query.created_to: + _created_to = datetime.strptime( + get_query.created_to + ".999999", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfoHistory.created + <= local_tz.localize(_created_to).astimezone(utc_tz) + ) + + count = await db.scalar(select(func.count()).select_from(stmt.subquery())) + + # Sort + if get_query.sort_order == 0: + stmt = stmt.order_by(IDXPersonalInfoHistory.block_timestamp) + else: + stmt = stmt.order_by(desc(IDXPersonalInfoHistory.block_timestamp)) + + # Pagination + if get_query.limit is not None: + stmt = stmt.limit(get_query.limit) + if get_query.offset is not None: + stmt = stmt.offset(get_query.offset) + + history_list: Sequence[IDXPersonalInfoHistory] = (await db.scalars(stmt)).all() + data = [_history.json() for _history in history_list] + + return json_response( + { + "result_set": { + "count": count, + "offset": get_query.offset, + "limit": get_query.limit, "total": total, }, "personal_info": data, diff --git a/app/utils/check_utils.py b/app/utils/check_utils.py index 88b22e85..9fbb3be9 100644 --- a/app/utils/check_utils.py +++ b/app/utils/check_utils.py @@ -18,7 +18,7 @@ """ import hashlib -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from typing import Optional from fastapi import Request @@ -192,10 +192,7 @@ async def check_token_for_auth(db: AsyncSession, issuer_address: str, auth_token hashed_token = hashlib.sha256(auth_token.encode()).hexdigest() if issuer_token.auth_token != hashed_token: raise AuthorizationError - elif ( - issuer_token.valid_duration != 0 - and issuer_token.usage_start - + timedelta(seconds=issuer_token.valid_duration) - < datetime.utcnow() - ): + elif issuer_token.valid_duration != 0 and issuer_token.usage_start + timedelta( + seconds=issuer_token.valid_duration + ) < datetime.now(UTC).replace(tzinfo=None): raise AuthorizationError diff --git a/app/utils/contract_error_code.py b/app/utils/contract_error_code.py index 56ec0539..27ec004f 100644 --- a/app/utils/contract_error_code.py +++ b/app/utils/contract_error_code.py @@ -157,6 +157,34 @@ 240303: "Message sender is not escrow agent.", 240304: "Token status of escrow is inactive.", 240401: "Message sender balance is insufficient.", + # DVPStorage (25XXXX) + 250001: "Message sender(exchange contract) isn't latest version.", + # IbetSecurityTokenDVP (26XXXX) + 260001: "Delivery amount is 0.", + 260002: "Message sender balance is insufficient.", + 260003: "Token status of delivery is inactive.", + 260004: "Token transferApprovalRequired of delivery is enabled.", + 260101: "Target delivery ID is invalid.", + 260102: "Target delivery status is invalid.", + 260103: "Target delivery has been confirmed.", + 260104: "Message sender is not the delivery seller nor the delivery buyer.", + 260201: "Target delivery ID is invalid.", + 260202: "Target delivery status is invalid.", + 260203: "Target delivery has been confirmed.", + 260204: "Message sender is not the delivery buyer.", + 260205: "Token status of delivery is inactive.", + 260206: "Token transferApprovalRequired of delivery is enabled.", + 260301: "Target delivery ID is invalid.", + 260302: "Target delivery status is invalid.", + 260303: "Target delivery has not been confirmed.", + 260304: "Message sender is not the delivery agent.", + 260305: "Token status of delivery is inactive.", + 260306: "Token transferApprovalRequired of delivery is enabled.", + 260401: "Target delivery ID is invalid.", + 260402: "Target delivery status is invalid.", + 260403: "Target delivery has been confirmed.", + 260404: "Message sender is not the delivery agent.", + 260501: "Message sender balance is insufficient.", # PaymentGateway (30XXXX) 300001: "Payment account is banned.", 300101: "Target account address is not registered.", diff --git a/app/utils/contract_utils.py b/app/utils/contract_utils.py index fc867742..91fb7f1f 100644 --- a/app/utils/contract_utils.py +++ b/app/utils/contract_utils.py @@ -202,13 +202,15 @@ def send_transaction(transaction: dict, private_key: bytes): transaction_dict=transaction, private_key=private_key ) # Send Transaction - tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction.hex()) + tx_hash = web3.eth.send_raw_transaction( + signed_tx.raw_transaction.to_0x_hex() + ) tx_receipt = web3.eth.wait_for_transaction_receipt( transaction_hash=tx_hash, timeout=10 ) if tx_receipt["status"] == 0: # inspect reason of transaction fail - code_msg = ContractUtils.inspect_tx_failure(tx_hash.hex()) + code_msg = ContractUtils.inspect_tx_failure(tx_hash.to_0x_hex()) raise ContractRevertError(code_msg=code_msg) except: raise @@ -216,7 +218,7 @@ def send_transaction(transaction: dict, private_key: bytes): local_session.rollback() # unlock record local_session.close() - return tx_hash.hex(), tx_receipt + return tx_hash.to_0x_hex(), tx_receipt @staticmethod def inspect_tx_failure(tx_hash: str) -> str: @@ -268,16 +270,16 @@ def get_event_logs( :param contract: Contract :param event: Event - :param block_from: fromBlock - :param block_to: toBlock + :param block_from: from_block + :param block_to: to_block :param argument_filters: Argument filter :return: Event logs """ try: _event = getattr(contract.events, event) result = _event.get_logs( - fromBlock=block_from, - toBlock=block_to, + from_block=block_from, + to_block=block_to, argument_filters=argument_filters, ) except ABIEventFunctionNotFound: @@ -455,14 +457,16 @@ async def send_transaction(transaction: dict, private_key: bytes): ) # Send Transaction tx_hash = await async_web3.eth.send_raw_transaction( - signed_tx.rawTransaction.hex() + signed_tx.raw_transaction.to_0x_hex() ) tx_receipt = await async_web3.eth.wait_for_transaction_receipt( transaction_hash=tx_hash, timeout=10 ) if tx_receipt["status"] == 0: # inspect reason of transaction fail - code_msg = await AsyncContractUtils.inspect_tx_failure(tx_hash.hex()) + code_msg = await AsyncContractUtils.inspect_tx_failure( + tx_hash.to_0x_hex() + ) raise ContractRevertError(code_msg=code_msg) except: raise @@ -470,7 +474,7 @@ async def send_transaction(transaction: dict, private_key: bytes): await local_session.rollback() # unlock record await local_session.close() - return tx_hash.hex(), tx_receipt + return tx_hash.to_0x_hex(), tx_receipt @staticmethod async def inspect_tx_failure(tx_hash: str) -> str: @@ -522,16 +526,16 @@ async def get_event_logs( :param contract: Contract :param event: Event - :param block_from: fromBlock - :param block_to: toBlock + :param block_from: from_block + :param block_to: to_block :param argument_filters: Argument filter :return: Event logs """ try: _event = getattr(contract.events, event) result = await _event.get_logs( - fromBlock=block_from, - toBlock=block_to, + from_block=block_from, + to_block=block_to, argument_filters=argument_filters, ) except ABIEventFunctionNotFound: diff --git a/app/utils/e2ee_utils.py b/app/utils/e2ee_utils.py index 5708da63..e34cb9fd 100644 --- a/app/utils/e2ee_utils.py +++ b/app/utils/e2ee_utils.py @@ -19,7 +19,7 @@ import base64 import binascii -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta import boto3 from Crypto.Cipher import PKCS1_OAEP @@ -118,7 +118,9 @@ def __get_crypto_data() -> DictCache: ) # Use Cache - if E2EEUtils.cache.get("expiration_datetime") > datetime.utcnow(): + if E2EEUtils.cache.get("expiration_datetime") > datetime.now(UTC).replace( + tzinfo=None + ): return E2EEUtils.cache # Get Private Key @@ -148,7 +150,8 @@ def __get_crypto_data() -> DictCache: "private_key": private_key, "public_key": public_key, "encrypted_length": encrypted_length, - "expiration_datetime": datetime.utcnow() + timedelta(hours=1), + "expiration_datetime": datetime.now(UTC).replace(tzinfo=None) + + timedelta(hours=1), } ) diff --git a/app/utils/ledger_utils.py b/app/utils/ledger_utils.py index e94371ba..3c4a0f8e 100644 --- a/app/utils/ledger_utils.py +++ b/app/utils/ledger_utils.py @@ -18,7 +18,7 @@ """ import uuid -from datetime import datetime +from datetime import UTC, datetime from typing import Sequence import pytz @@ -26,6 +26,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.model.blockchain import ( + ContractPersonalInfoType, IbetShareContract, IbetStraightBondContract, PersonalInfoContract, @@ -93,21 +94,22 @@ async def create_ledger(token_address: str, db: AsyncSession): for _details in _details_list: # Get ledger details data - data_list = await __get_details_data_list( + data_list, some_personal_info_not_registered = await __get_details_data_list( token_address, _token.type, _details.data_type, _details.data_source, db ) - - # NOTE: Merge with template with ledger GET API details = { "token_detail_type": _details.token_detail_type, "headers": _details.headers, "data": data_list, "footers": _details.footers, + "some_personal_info_not_registered": some_personal_info_not_registered, } ledger_details.append(details) created_ymd = ( - utc_tz.localize(datetime.utcnow()).astimezone(local_tz).strftime("%Y/%m/%d") + utc_tz.localize(datetime.now(UTC).replace(tzinfo=None)) + .astimezone(local_tz) + .strftime("%Y/%m/%d") ) # NOTE: Merge with template with ledger GET API ledger = { @@ -153,8 +155,9 @@ async def __get_details_data_list( data_type: str, data_source: str, db: AsyncSession, -): +) -> tuple[list[dict], bool]: data_list = [] + some_personal_info_not_registered = False if data_type == LedgerDetailsDataType.DB.value: data_list = [] # Get Ledger Details Data from DB @@ -183,16 +186,19 @@ async def __get_details_data_list( } ) elif data_type == LedgerDetailsDataType.IBET_FIN.value: - data_list = await __get_details_data_list_from_ibetfin( - token_address, token_type, db + # NOTE: + # If there is an account with no personal information registered, + # some_personal_info_not_registered will be True. + data_list, some_personal_info_not_registered = ( + await __get_details_data_list_from_ibetfin(token_address, token_type, db) ) - return data_list + return data_list, some_personal_info_not_registered async def __get_details_data_list_from_ibetfin( token_address: str, token_type: str, db: AsyncSession -): +) -> tuple[list[dict], bool]: if token_type == TokenType.IBET_SHARE.value: token_contract = await IbetShareContract(token_address).get() price = token_contract.principal_value @@ -245,6 +251,7 @@ async def __get_details_data_list_from_ibetfin( utxo_grouped[_utxo.account_address][date_ymd] += _utxo.amount data_list = [] + some_personal_info_not_registered = False for account_address, date_ymd_amount in utxo_grouped.items(): for date_ymd, amount in date_ymd_amount.items(): details_data = { @@ -258,9 +265,9 @@ async def __get_details_data_list_from_ibetfin( } # Update PersonalInfo - personal_info = await __get_personal_info( + personal_info, _pi_not_registered = await __get_personal_info( account_address, - token_contract.issuer_address, + token_contract, personal_info_contract, db, ) @@ -268,34 +275,71 @@ async def __get_details_data_list_from_ibetfin( details_data["address"] = personal_info.get("address", None) data_list.append(details_data) + if _pi_not_registered: + some_personal_info_not_registered = True - return data_list + return data_list, some_personal_info_not_registered async def __get_personal_info( account_address: str, - issuer_address: str, + token_contract: IbetShareContract | IbetStraightBondContract, personal_info_contract: PersonalInfoContract, db: AsyncSession, -): +) -> tuple[dict, bool]: + # NOTE: + # For tokens with require_personal_info_registered = False, search only indexed data. + # If indexed data does not exist, return the default value. + + # Issuer cannot have any personal info + if account_address == token_contract.issuer_address: + personal_info_not_registered = False + return ( + ContractPersonalInfoType( + key_manager=None, + name=None, + address=None, + postal_code=None, + email=None, + birth=None, + ).model_dump(), + personal_info_not_registered, + ) + + # Search indexed data _idx_personal_info: IDXPersonalInfo | None = ( await db.scalars( select(IDXPersonalInfo) .where( and_( IDXPersonalInfo.account_address == account_address, - IDXPersonalInfo.issuer_address == issuer_address, + IDXPersonalInfo.issuer_address == token_contract.issuer_address, ) ) .limit(1) ) ).first() + if ( + _idx_personal_info is not None + and any(_idx_personal_info.personal_info.values()) is not False + ): + # Get personal info from DB + personal_info_not_registered = False + return _idx_personal_info.personal_info, personal_info_not_registered - if _idx_personal_info is None: # Get PersonalInfo to Contract + # Retrieve personal info + if token_contract.require_personal_info_registered is True: + # Retrieve from contract storage personal_info = await personal_info_contract.get_info( account_address, default_value=None ) + if any(personal_info.values()) is False: + personal_info_not_registered = True + else: + personal_info_not_registered = False else: - personal_info = _idx_personal_info.personal_info + # Do not retrieve contract data and return the default value + personal_info = ContractPersonalInfoType().model_dump() + personal_info_not_registered = True - return personal_info + return personal_info, personal_info_not_registered diff --git a/app/utils/web3_utils.py b/app/utils/web3_utils.py index 379e39e6..32b1e78c 100644 --- a/app/utils/web3_utils.py +++ b/app/utils/web3_utils.py @@ -30,10 +30,10 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import Session -from web3 import AsyncWeb3, Web3 +from web3 import AsyncHTTPProvider, AsyncWeb3, HTTPProvider, Web3 from web3.eth import AsyncEth from web3.geth import AsyncGeth -from web3.middleware import async_geth_poa_middleware, geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from web3.net import AsyncNet from web3.types import RPCEndpoint, RPCResponse @@ -77,7 +77,7 @@ def _get_web3(request_timeout: int) -> Web3: web3 = Web3( FailOverHTTPProvider(request_kwargs={"timeout": request_timeout}) ) - web3.middleware_onion.inject(geth_poa_middleware, layer=0) + web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) thread_local.web3 = web3 return web3 @@ -115,13 +115,13 @@ def _get_web3(request_timeout: int) -> AsyncWeb3: async_web3 = AsyncWeb3( AsyncFailOverHTTPProvider(request_kwargs={"timeout": request_timeout}) ) - async_web3.middleware_onion.inject(async_geth_poa_middleware, layer=0) + async_web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) thread_local.async_web3 = async_web3 return async_web3 -class FailOverHTTPProvider(Web3.HTTPProvider): +class FailOverHTTPProvider(HTTPProvider): fail_over_mode = False # If False, use only the default(primary) provider def __init__(self, *args, **kwargs): @@ -180,7 +180,7 @@ def set_fail_over_mode(use_fail_over: bool): FailOverHTTPProvider.fail_over_mode = use_fail_over -class AsyncFailOverHTTPProvider(Web3.AsyncHTTPProvider): +class AsyncFailOverHTTPProvider(AsyncHTTPProvider): fail_over_mode = False # If False, use only the default(primary) provider def __init__(self, *args, **kwargs): diff --git a/batch/indexer_block_tx_data.py b/batch/indexer_block_tx_data.py index 2fce52e7..ccfc72da 100644 --- a/batch/indexer_block_tx_data.py +++ b/batch/indexer_block_tx_data.py @@ -68,23 +68,25 @@ async def process(self): # Synchronize block data block_model = IDXBlockData() block_model.number = block_data.get("number") - block_model.parent_hash = block_data.get("parentHash").hex() - block_model.sha3_uncles = block_data.get("sha3Uncles").hex() + block_model.parent_hash = block_data.get("parentHash").to_0x_hex() + block_model.sha3_uncles = block_data.get("sha3Uncles").to_0x_hex() block_model.miner = block_data.get("miner") - block_model.state_root = block_data.get("stateRoot").hex() - block_model.transactions_root = block_data.get("transactionsRoot").hex() - block_model.receipts_root = block_data.get("receiptsRoot").hex() - block_model.logs_bloom = block_data.get("logsBloom").hex() + block_model.state_root = block_data.get("stateRoot").to_0x_hex() + block_model.transactions_root = block_data.get( + "transactionsRoot" + ).to_0x_hex() + block_model.receipts_root = block_data.get("receiptsRoot").to_0x_hex() + block_model.logs_bloom = block_data.get("logsBloom").to_0x_hex() block_model.difficulty = block_data.get("difficulty") block_model.gas_limit = block_data.get("gasLimit") block_model.gas_used = block_data.get("gasUsed") block_model.timestamp = block_data.get("timestamp") block_model.proof_of_authority_data = block_data.get( "proofOfAuthorityData" - ).hex() - block_model.mix_hash = block_data.get("mixHash").hex() - block_model.nonce = block_data.get("nonce").hex() - block_model.hash = block_data.get("hash").hex() + ).to_0x_hex() + block_model.mix_hash = block_data.get("mixHash").to_0x_hex() + block_model.nonce = block_data.get("nonce").to_0x_hex() + block_model.hash = block_data.get("hash").to_0x_hex() block_model.size = block_data.get("size") transactions: Sequence[TxData] = block_data.get("transactions") @@ -92,8 +94,8 @@ async def process(self): for transaction in transactions: # Synchronize tx data tx_model = IDXTxData() - tx_model.hash = transaction.get("hash").hex() - tx_model.block_hash = transaction.get("blockHash").hex() + tx_model.hash = transaction.get("hash").to_0x_hex() + tx_model.block_hash = transaction.get("blockHash").to_0x_hex() tx_model.block_number = transaction.get("blockNumber") tx_model.transaction_index = transaction.get("transactionIndex") tx_model.from_address = to_checksum_address(transaction.get("from")) @@ -102,14 +104,14 @@ async def process(self): if transaction.get("to") else None ) - tx_model.input = transaction.get("input").hex() + tx_model.input = transaction.get("input").to_0x_hex() tx_model.gas = transaction.get("gas") tx_model.gas_price = transaction.get("gasPrice") tx_model.value = transaction.get("value") tx_model.nonce = transaction.get("nonce") local_session.add(tx_model) - transaction_hash_list.append(transaction.get("hash").hex()) + transaction_hash_list.append(transaction.get("hash").to_0x_hex()) block_model.transactions = transaction_hash_list local_session.add(block_model) diff --git a/batch/indexer_delivery.py b/batch/indexer_delivery.py new file mode 100644 index 00000000..a3b1be72 --- /dev/null +++ b/batch/indexer_delivery.py @@ -0,0 +1,597 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import asyncio +import sys +from datetime import UTC, datetime +from typing import Literal, Optional, Sequence + +import uvloop +from sqlalchemy import and_, select +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.ext.asyncio import AsyncSession +from web3.contract import AsyncContract + +from app.database import BatchAsyncSessionLocal +from app.exceptions import ServiceUnavailableError +from app.model.blockchain import IbetShareContract, IbetStraightBondContract +from app.model.db import ( + Account, + DeliveryStatus, + IDXDelivery, + IDXDeliveryBlockNumber, + Token, + TokenType, +) +from app.utils.contract_utils import AsyncContractUtils +from app.utils.web3_utils import AsyncWeb3Wrapper +from batch import batch_log +from config import INDEXER_BLOCK_LOT_MAX_SIZE, INDEXER_SYNC_INTERVAL, ZERO_ADDRESS + +process_name = "INDEXER-Delivery" +LOG = batch_log.get_logger(process_name=process_name) + +web3 = AsyncWeb3Wrapper() + + +""" +Batch process for indexing security token dvp delivery events + +ibetSecurityTokenDVP + - CreateDelivery + - CancelDelivery + - ConfirmDelivery + - FinishDelivery + - AbortDelivery +""" + + +class Processor: + def __init__(self): + self.token_list: list[str] = [] + self.exchange_list: list[AsyncContract] = [] + + async def sync_new_logs(self): + db_session = BatchAsyncSessionLocal() + try: + await self.__get_contract_list(db_session=db_session) + + latest_block = await web3.eth.block_number + for contract in self.exchange_list: + # Get from_block_number and to_block_number for contract event filter + _from_block = await self.__get_idx_delivery_block_number( + db_session=db_session, exchange_address=contract.address + ) + _to_block = _from_block + INDEXER_BLOCK_LOT_MAX_SIZE + + # Skip processing if the latest block is not counted up + if _from_block >= latest_block: + LOG.debug("skip process") + return + + # Create index data with the upper limit of one process + # as INDEXER_BLOCK_LOT_MAX_SIZE(1_000_000 blocks) + if latest_block > _to_block: + while _to_block < latest_block: + await self.__sync_all( + db_session=db_session, + block_from=_from_block + 1, + block_to=_to_block, + exchange=contract, + ) + _to_block += INDEXER_BLOCK_LOT_MAX_SIZE + _from_block += INDEXER_BLOCK_LOT_MAX_SIZE + await self.__sync_all( + db_session=db_session, + block_from=_from_block + 1, + block_to=latest_block, + exchange=contract, + ) + else: + await self.__sync_all( + db_session=db_session, + block_from=_from_block + 1, + block_to=latest_block, + exchange=contract, + ) + + await self.__set_idx_delivery_block_number( + db_session=db_session, + exchange_address=contract.address, + block_number=latest_block, + ) + await db_session.commit() + finally: + await db_session.close() + LOG.info("Sync job has been completed") + + async def __get_contract_list(self, db_session: AsyncSession): + """Get DVP contract list to index delivery event""" + issued_token_address_list: tuple[str, ...] = tuple( + [ + record[0] + for record in ( + await db_session.execute( + select(Token.token_address) + .join( + Account, + and_( + Account.issuer_address == Token.issuer_address, + Account.is_deleted == False, + ), + ) + .where(Token.token_status == 1) + ) + ) + .tuples() + .all() + ] + ) + loaded_exchange_address_list: tuple[str, ...] = tuple(self.token_list) + load_required_address_list = list( + set(issued_token_address_list) ^ set(loaded_exchange_address_list) + ) + + load_required_token_list: Sequence[Token] = ( + await db_session.scalars( + select(Token).where( + and_( + Token.token_status == 1, + Token.token_address.in_(load_required_address_list), + ) + ) + ) + ).all() + + _exchange_list_tmp = [] + for load_required_token in load_required_token_list: + self.token_list.append(load_required_token.token_address) + token_contract = web3.eth.contract( + address=load_required_token.token_address, abi=load_required_token.abi + ) + tradable_exchange_address = ZERO_ADDRESS + if load_required_token.type == TokenType.IBET_STRAIGHT_BOND.value: + bond_token = IbetStraightBondContract(token_contract.address) + await bond_token.get() + tradable_exchange_address = ( + bond_token.tradable_exchange_contract_address + ) + elif load_required_token.type == TokenType.IBET_SHARE.value: + share_token = IbetShareContract(token_contract.address) + await share_token.get() + tradable_exchange_address = ( + share_token.tradable_exchange_contract_address + ) + + if tradable_exchange_address != ZERO_ADDRESS: + _exchange_list_tmp.append(tradable_exchange_address) + + # Remove duplicate exchanges from a list + loaded_exchange_address_list: tuple[str, ...] = tuple( + [exchange.address for exchange in self.exchange_list] + ) + for _exchange_address in list(set(_exchange_list_tmp)): + if _exchange_address not in loaded_exchange_address_list: + exchange_contract = AsyncContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", + contract_address=_exchange_address, + ) + self.exchange_list.append(exchange_contract) + + @staticmethod + async def __get_idx_delivery_block_number( + db_session: AsyncSession, exchange_address: str + ): + """Get block number of delivery index""" + _idx_delivery_block_number = ( + await db_session.scalars( + select(IDXDeliveryBlockNumber) + .where(IDXDeliveryBlockNumber.exchange_address == exchange_address) + .limit(1) + ) + ).first() + if _idx_delivery_block_number is None: + return 0 + else: + return _idx_delivery_block_number.latest_block_number + + @staticmethod + async def __set_idx_delivery_block_number( + db_session: AsyncSession, + exchange_address: str, + block_number: int, + ): + """Set block number of delivery index""" + _idx_delivery_block_number = ( + await db_session.scalars( + select(IDXDeliveryBlockNumber) + .where(IDXDeliveryBlockNumber.exchange_address == exchange_address) + .limit(1) + ) + ).first() + if _idx_delivery_block_number is None: + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = block_number + _idx_delivery_block_number.exchange_address = exchange_address + await db_session.merge(_idx_delivery_block_number) + + async def __sync_all( + self, + db_session: AsyncSession, + block_from: int, + block_to: int, + exchange: AsyncContract, + ): + LOG.info( + f"Syncing from={block_from}, to={block_to}, exchange={exchange.address}" + ) + await self.__sync_delivery_created( + db_session, block_from, block_to, exchange=exchange + ) + await self.__sync_delivery_canceled( + db_session, block_from, block_to, exchange=exchange + ) + await self.__sync_delivery_confirmed( + db_session, block_from, block_to, exchange=exchange + ) + await self.__sync_delivery_finished( + db_session, block_from, block_to, exchange=exchange + ) + await self.__sync_delivery_aborted( + db_session, block_from, block_to, exchange=exchange + ) + + async def __sync_delivery_created( + self, + db_session: AsyncSession, + block_from: int, + block_to: int, + exchange: AsyncContract, + ): + """Sync DeliveryCreated events of IbetSecurityTokenDVP + :param db_session: ORM session + :param block_to: To Block + :return: None + """ + if block_from > block_to: + return + try: + events = await AsyncContractUtils.get_event_logs( + contract=exchange, + event="DeliveryCreated", + block_from=block_from, + block_to=block_to, + ) + for event in events: + transaction_hash = event["transactionHash"].to_0x_hex() + args = event["args"] + amount = args.get("amount", 0) + if amount > sys.maxsize: # suppress overflow + continue + block_timestamp = await self.__get_block_timestamp(event=event) + await self.__sink_on_delivery( + db_session=db_session, + event_type="Created", + exchange_address=exchange.address, + delivery_id=args.get("deliveryId"), + token_address=args.get("token", ZERO_ADDRESS), + seller_address=args.get("seller", ZERO_ADDRESS), + buyer_address=args.get("buyer", ZERO_ADDRESS), + amount=amount, + agent_address=args.get("agent", ZERO_ADDRESS), + block_timestamp=block_timestamp, + transaction_hash=transaction_hash, + data=args.get("data"), + ) + except Exception: + raise + + async def __sync_delivery_canceled( + self, + db_session: AsyncSession, + block_from: int, + block_to: int, + exchange: AsyncContract, + ): + """Sync DeliveryCanceled events of IbetSecurityTokenDVP + :param db_session: ORM session + :param block_to: To Block + :return: None + """ + if block_from > block_to: + return + try: + events = await AsyncContractUtils.get_event_logs( + contract=exchange, + event="DeliveryCanceled", + block_from=block_from, + block_to=block_to, + ) + for event in events: + transaction_hash = event["transactionHash"].to_0x_hex() + args = event["args"] + amount = args.get("amount", 0) + if amount > sys.maxsize: # suppress overflow + continue + block_timestamp = await self.__get_block_timestamp(event=event) + await self.__sink_on_delivery( + db_session=db_session, + event_type="Canceled", + exchange_address=exchange.address, + delivery_id=args.get("deliveryId"), + token_address=args.get("token", ZERO_ADDRESS), + seller_address=args.get("seller", ZERO_ADDRESS), + buyer_address=args.get("buyer", ZERO_ADDRESS), + amount=amount, + agent_address=args.get("agent", ZERO_ADDRESS), + block_timestamp=block_timestamp, + transaction_hash=transaction_hash, + ) + except Exception: + raise + + async def __sync_delivery_confirmed( + self, + db_session: AsyncSession, + block_from: int, + block_to: int, + exchange: AsyncContract, + ): + """Sync DeliveryConfirmed events of IbetSecurityTokenDVP + :param db_session: ORM session + :param block_to: To Block + :return: None + """ + if block_from > block_to: + return + try: + events = await AsyncContractUtils.get_event_logs( + contract=exchange, + event="DeliveryConfirmed", + block_from=block_from, + block_to=block_to, + ) + for event in events: + transaction_hash = event["transactionHash"].to_0x_hex() + args = event["args"] + amount = args.get("amount", 0) + if amount > sys.maxsize: # suppress overflow + continue + block_timestamp = await self.__get_block_timestamp(event=event) + await self.__sink_on_delivery( + db_session=db_session, + event_type="Confirmed", + exchange_address=exchange.address, + delivery_id=args.get("deliveryId"), + token_address=args.get("token", ZERO_ADDRESS), + seller_address=args.get("seller", ZERO_ADDRESS), + buyer_address=args.get("buyer", ZERO_ADDRESS), + amount=amount, + agent_address=args.get("agent", ZERO_ADDRESS), + block_timestamp=block_timestamp, + transaction_hash=transaction_hash, + ) + except Exception: + raise + + async def __sync_delivery_finished( + self, + db_session: AsyncSession, + block_from: int, + block_to: int, + exchange: AsyncContract, + ): + """Sync DeliveryFinished events of IbetSecurityTokenDVP + :param db_session: ORM session + :param block_to: To Block + :return: None + """ + if block_from > block_to: + return + try: + events = await AsyncContractUtils.get_event_logs( + contract=exchange, + event="DeliveryFinished", + block_from=block_from, + block_to=block_to, + ) + for event in events: + transaction_hash = event["transactionHash"].to_0x_hex() + args = event["args"] + amount = args.get("amount", 0) + if amount > sys.maxsize: # suppress overflow + continue + block_timestamp = await self.__get_block_timestamp(event=event) + await self.__sink_on_delivery( + db_session=db_session, + event_type="Finished", + exchange_address=exchange.address, + delivery_id=args.get("deliveryId"), + token_address=args.get("token", ZERO_ADDRESS), + seller_address=args.get("seller", ZERO_ADDRESS), + buyer_address=args.get("buyer", ZERO_ADDRESS), + amount=amount, + agent_address=args.get("agent", ZERO_ADDRESS), + block_timestamp=block_timestamp, + transaction_hash=transaction_hash, + ) + except Exception: + raise + + async def __sync_delivery_aborted( + self, + db_session: AsyncSession, + block_from: int, + block_to: int, + exchange: AsyncContract, + ): + """Sync DeliveryAborted events of IbetSecurityTokenDVP + :param db_session: ORM session + :param block_to: To Block + :return: None + """ + if block_from > block_to: + return + try: + events = await AsyncContractUtils.get_event_logs( + contract=exchange, + event="DeliveryAborted", + block_from=block_from, + block_to=block_to, + ) + for event in events: + transaction_hash = event["transactionHash"].to_0x_hex() + args = event["args"] + amount = args.get("amount", 0) + if amount > sys.maxsize: # suppress overflow + continue + block_timestamp = await self.__get_block_timestamp(event=event) + await self.__sink_on_delivery( + db_session=db_session, + event_type="Aborted", + exchange_address=exchange.address, + delivery_id=args.get("deliveryId"), + token_address=args.get("token", ZERO_ADDRESS), + seller_address=args.get("seller", ZERO_ADDRESS), + buyer_address=args.get("buyer", ZERO_ADDRESS), + amount=amount, + agent_address=args.get("agent", ZERO_ADDRESS), + block_timestamp=block_timestamp, + transaction_hash=transaction_hash, + ) + except Exception: + raise + + @staticmethod + async def __get_block_timestamp(event) -> int: + block_timestamp = (await web3.eth.get_block(event["blockNumber"]))["timestamp"] + return block_timestamp + + @staticmethod + async def __sink_on_delivery( + db_session: AsyncSession, + event_type: Literal["Created", "Canceled", "Confirmed", "Finished", "Aborted"], + exchange_address: str, + delivery_id: int, + token_address: str, + buyer_address: str, + seller_address: str, + amount: int, + agent_address: str, + block_timestamp: int, + transaction_hash: str, + data: Optional[str] = None, + ): + """Update Delivery data in DB + :param db_session: ORM session + :param event_type: event type ["Created", "Canceled", "Confirmed", "Finished", "Aborted"] + :param token_address: token address + :param exchange_address: exchange address + :param delivery_id: delivery id + :param buyer_address: delivery buyer address + :param seller_address: delivery seller address + :param amount: delivery amount + :param agent_address: delivery agent address + :param data: optional data (Created) + :param block_timestamp: block timestamp + :return: None + """ + delivery = ( + await db_session.scalars( + select(IDXDelivery) + .where(IDXDelivery.exchange_address == exchange_address) + .where(IDXDelivery.delivery_id == delivery_id) + .limit(1) + ) + ).first() + if event_type == "Created": + if delivery is None: + delivery = IDXDelivery() + delivery.exchange_address = exchange_address + delivery.delivery_id = delivery_id + delivery.token_address = token_address + delivery.buyer_address = buyer_address + delivery.seller_address = seller_address + delivery.amount = amount + delivery.agent_address = agent_address + delivery.data = data + delivery.create_blocktimestamp = datetime.fromtimestamp( + block_timestamp, tz=UTC + ) + delivery.create_transaction_hash = transaction_hash + delivery.confirmed = False + delivery.valid = True + delivery.status = DeliveryStatus.DELIVERY_CREATED + elif event_type == "Canceled": + if delivery is not None: + delivery.cancel_blocktimestamp = datetime.fromtimestamp( + block_timestamp, tz=UTC + ) + delivery.cancel_transaction_hash = transaction_hash + delivery.valid = False + delivery.status = DeliveryStatus.DELIVERY_CANCELED + elif event_type == "Confirmed": + if delivery is not None: + delivery.confirm_blocktimestamp = datetime.fromtimestamp( + block_timestamp, tz=UTC + ) + delivery.confirm_transaction_hash = transaction_hash + delivery.confirmed = True + delivery.status = DeliveryStatus.DELIVERY_CONFIRMED + elif event_type == "Finished": + if delivery is not None: + delivery.finish_blocktimestamp = datetime.fromtimestamp( + block_timestamp, tz=UTC + ) + delivery.finish_transaction_hash = transaction_hash + delivery.valid = False + delivery.status = DeliveryStatus.DELIVERY_FINISHED + elif event_type == "Aborted": + if delivery is not None: + delivery.abort_blocktimestamp = datetime.fromtimestamp( + block_timestamp, tz=UTC + ) + delivery.abort_transaction_hash = transaction_hash + delivery.valid = False + delivery.status = DeliveryStatus.DELIVERY_ABORTED + await db_session.merge(delivery) + + +async def main(): + LOG.info("Service started successfully") + processor = Processor() + + while True: + try: + await processor.sync_new_logs() + except ServiceUnavailableError: + LOG.warning("An external service was unavailable") + except SQLAlchemyError as sa_err: + LOG.error(f"A database error has occurred: code={sa_err.code}\n{sa_err}") + except Exception as ex: + LOG.error(ex) + + await asyncio.sleep(INDEXER_SYNC_INTERVAL) + + +if __name__ == "__main__": + try: + uvloop.run(main()) + except KeyboardInterrupt: + sys.exit(1) diff --git a/batch/indexer_e2e_messaging.py b/batch/indexer_e2e_messaging.py index 7bf01e8f..aed1cf62 100644 --- a/batch/indexer_e2e_messaging.py +++ b/batch/indexer_e2e_messaging.py @@ -22,7 +22,7 @@ import json import sys import time -from datetime import datetime +from datetime import UTC, datetime import uvloop from Crypto.Cipher import AES, PKCS1_OAEP @@ -172,14 +172,16 @@ async def __sync_message( block_to=block_to, ) for event in events: - transaction_hash = event["transactionHash"].hex() - block_timestamp = datetime.utcfromtimestamp( - (await web3.eth.get_block(event["blockNumber"]))["timestamp"] - ) + transaction_hash = event["transactionHash"].to_0x_hex() + block_timestamp = datetime.fromtimestamp( + (await web3.eth.get_block(event["blockNumber"]))["timestamp"], UTC + ).replace(tzinfo=None) args = event["args"] from_address = args["sender"] to_address = args["receiver"] - send_timestamp = datetime.utcfromtimestamp(args["time"]) + send_timestamp = datetime.fromtimestamp(args["time"], UTC).replace( + tzinfo=None + ) text = args["text"] # Check if the message receiver is owned accounts @@ -211,7 +213,7 @@ async def __sync_message( block_timestamp=block_timestamp, ) except Exception as e: - LOG.error(e) + raise @staticmethod async def __get_e2e_messaging_account(db_session: AsyncSession, to_address: str): diff --git a/batch/indexer_issue_redeem.py b/batch/indexer_issue_redeem.py index 8d3cb717..52079f72 100644 --- a/batch/indexer_issue_redeem.py +++ b/batch/indexer_issue_redeem.py @@ -19,7 +19,7 @@ import asyncio import sys -from datetime import datetime +from datetime import UTC, datetime from typing import Sequence import uvloop @@ -197,10 +197,11 @@ async def __sync_issue( ) for event in events: args = event["args"] - transaction_hash = event["transactionHash"].hex() - block_timestamp = datetime.utcfromtimestamp( - (await web3.eth.get_block(event["blockNumber"]))["timestamp"] - ) + transaction_hash = event["transactionHash"].to_0x_hex() + block_timestamp = datetime.fromtimestamp( + (await web3.eth.get_block(event["blockNumber"]))["timestamp"], + UTC, + ).replace(tzinfo=None) if args["amount"] > sys.maxsize: pass else: @@ -215,7 +216,7 @@ async def __sync_issue( block_timestamp=block_timestamp, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __sync_redeem( self, db_session: AsyncSession, block_from: int, block_to: int @@ -237,10 +238,11 @@ async def __sync_redeem( ) for event in events: args = event["args"] - transaction_hash = event["transactionHash"].hex() - block_timestamp = datetime.utcfromtimestamp( - (await web3.eth.get_block(event["blockNumber"]))["timestamp"] - ) + transaction_hash = event["transactionHash"].to_0x_hex() + block_timestamp = datetime.fromtimestamp( + (await web3.eth.get_block(event["blockNumber"]))["timestamp"], + UTC, + ).replace(tzinfo=None) if args["amount"] > sys.maxsize: pass else: @@ -255,7 +257,7 @@ async def __sync_redeem( block_timestamp=block_timestamp, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise @staticmethod async def __insert_index( diff --git a/batch/indexer_personal_info.py b/batch/indexer_personal_info.py index cb46bb4b..f16a523b 100644 --- a/batch/indexer_personal_info.py +++ b/batch/indexer_personal_info.py @@ -20,7 +20,7 @@ import asyncio import json import sys -from datetime import datetime +from datetime import UTC, datetime from typing import Sequence import uvloop @@ -40,6 +40,8 @@ Account, IDXPersonalInfo, IDXPersonalInfoBlockNumber, + IDXPersonalInfoHistory, + PersonalInfoEventType, Token, TokenType, ) @@ -203,7 +205,9 @@ async def __sync_personal_info_register( link_address = args.get("link_address", ZERO_ADDRESS) if link_address == _personal_info_contract.issuer.issuer_address: block = await web3.eth.get_block(event["blockNumber"]) - timestamp = datetime.utcfromtimestamp(block["timestamp"]) + timestamp = datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) decrypted_personal_info = ( await _personal_info_contract.get_info( account_address=account_address, default_value=None @@ -213,12 +217,13 @@ async def __sync_personal_info_register( db_session=db_session, account_address=account_address, issuer_address=link_address, + event_type=PersonalInfoEventType.REGISTER, personal_info=decrypted_personal_info, timestamp=timestamp, ) await db_session.commit() except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __sync_personal_info_modify( self, db_session: AsyncSession, block_from, block_to @@ -234,7 +239,9 @@ async def __sync_personal_info_modify( link_address = args.get("link_address", ZERO_ADDRESS) if link_address == _personal_info_contract.issuer.issuer_address: block = await web3.eth.get_block(event["blockNumber"]) - timestamp = datetime.utcfromtimestamp(block["timestamp"]) + timestamp = datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) decrypted_personal_info = ( await _personal_info_contract.get_info( account_address=account_address, default_value=None @@ -244,18 +251,20 @@ async def __sync_personal_info_modify( db_session=db_session, account_address=account_address, issuer_address=link_address, + event_type=PersonalInfoEventType.MODIFY, personal_info=decrypted_personal_info, timestamp=timestamp, ) await db_session.commit() except Exception: - LOG.exception("An exception occurred during event synchronization") + raise @staticmethod async def __sink_on_personal_info( db_session: AsyncSession, account_address: str, issuer_address: str, + event_type: PersonalInfoEventType, personal_info: dict, timestamp: datetime, ): @@ -292,6 +301,14 @@ async def __sink_on_personal_info( f"Register: account_address={account_address}, issuer_address={issuer_address}" ) + _personal_info_history = IDXPersonalInfoHistory() + _personal_info_history.account_address = account_address + _personal_info_history.issuer_address = issuer_address + _personal_info_history.event_type = event_type + _personal_info_history.personal_info = _personal_info.personal_info + _personal_info_history.block_timestamp = timestamp + db_session.add(_personal_info_history) + async def main(): LOG.info("Service started successfully") diff --git a/batch/indexer_position_bond.py b/batch/indexer_position_bond.py index 114d3a57..719565b3 100644 --- a/batch/indexer_position_bond.py +++ b/batch/indexer_position_bond.py @@ -223,6 +223,7 @@ async def __sync_all( await self.__sync_approve_transfer(db_session, block_from, block_to) await self.__sync_exchange(db_session, block_from, block_to) await self.__sync_escrow(db_session, block_from, block_to) + await self.__sync_dvp(db_session, block_from, block_to) async def __sync_issuer(self, db_session: AsyncSession): """Synchronize issuer position""" @@ -317,7 +318,7 @@ async def __sync_transfer( args.get("from", ZERO_ADDRESS), args.get("to", ZERO_ADDRESS), ]: - if (await web3.eth.get_code(account)).hex() == "0x": + if (await web3.eth.get_code(account)).to_0x_hex() == "0x": ( balance, pending_transfer, @@ -371,7 +372,7 @@ async def __sync_lock( # Index Lock event await self.__insert_lock_idx( db_session=db_session, - transaction_hash=event["transactionHash"].hex(), + transaction_hash=event["transactionHash"].to_0x_hex(), msg_sender=msg_sender, block_number=event["blockNumber"], token_address=token.address, @@ -479,7 +480,7 @@ async def __sync_unlock( # Index Unlock event await self.__insert_unlock_idx( db_session=db_session, - transaction_hash=event["transactionHash"].hex(), + transaction_hash=event["transactionHash"].to_0x_hex(), msg_sender=msg_sender, block_number=event["blockNumber"], token_address=token.address, @@ -979,6 +980,131 @@ async def __sync_escrow( except Exception as e: raise e + async def __sync_dvp( + self, db_session: AsyncSession, block_from: int, block_to: int + ): + """Sync Events from IbetSecurityTokenDVP + + :param db_session: database session + :param block_from: From block + :param block_to: To block + :return: None + """ + for exchange_address in self.exchange_address_list: + try: + dvp = AsyncContractUtils.get_contract( + "IbetSecurityTokenDVP", exchange_address + ) + + account_list_tmp = [] + + # DeliveryCreated event + _event_list = await AsyncContractUtils.get_event_logs( + contract=dvp, + event="DeliveryCreated", + block_from=block_from, + block_to=block_to, + ) + for _event in _event_list: + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get( + "seller", ZERO_ADDRESS + ), # only seller has changed + } + ) + + # DeliveryCanceled event + _event_list = await AsyncContractUtils.get_event_logs( + contract=dvp, + event="DeliveryCanceled", + block_from=block_from, + block_to=block_to, + ) + for _event in _event_list: + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get( + "seller", ZERO_ADDRESS + ), # only seller has changed + } + ) + + # DeliveryAborted event + _event_list = await AsyncContractUtils.get_event_logs( + contract=dvp, + event="DeliveryAborted", + block_from=block_from, + block_to=block_to, + ) + for _event in _event_list: + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get( + "seller", ZERO_ADDRESS + ), # only seller has changed + } + ) + + # HolderChanged event + _event_list = await AsyncContractUtils.get_event_logs( + contract=dvp, + event="HolderChanged", + block_from=block_from, + block_to=block_to, + ) + for _event in _event_list: + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get("from", ZERO_ADDRESS), + } + ) + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get("to", ZERO_ADDRESS), + } + ) + + # Make temporary list unique + account_list_tmp.sort( + key=lambda x: (x["token_address"], x["account_address"]) + ) + account_list = [] + for k, g in groupby( + account_list_tmp, + lambda x: (x["token_address"], x["account_address"]), + ): + account_list.append( + {"token_address": k[0], "account_address": k[1]} + ) + + # Update position + for _account in account_list: + token_address = _account["token_address"] + account_address = _account["account_address"] + ( + exchange_balance, + exchange_commitment, + ) = await self.__get_account_balance_exchange( + exchange_address=exchange_address, + token_address=token_address, + account_address=account_address, + ) + await self.__sink_on_position( + db_session=db_session, + token_address=token_address, + account_address=account_address, + exchange_balance=exchange_balance, + exchange_commitment=exchange_commitment, + ) + except Exception as e: + raise e + @staticmethod async def __insert_lock_idx( db_session: AsyncSession, diff --git a/batch/indexer_position_share.py b/batch/indexer_position_share.py index e17b80d0..818bf8aa 100644 --- a/batch/indexer_position_share.py +++ b/batch/indexer_position_share.py @@ -225,6 +225,7 @@ async def __sync_all( await self.__sync_approve_transfer(db_session, block_from, block_to) await self.__sync_exchange(db_session, block_from, block_to) await self.__sync_escrow(db_session, block_from, block_to) + await self.__sync_dvp(db_session, block_from, block_to) async def __sync_issuer(self, db_session: AsyncSession): """Synchronize issuer position""" @@ -319,7 +320,7 @@ async def __sync_transfer( args.get("from", ZERO_ADDRESS), args.get("to", ZERO_ADDRESS), ]: - if (await web3.eth.get_code(account)).hex() == "0x": + if (await web3.eth.get_code(account)).to_0x_hex() == "0x": ( balance, pending_transfer, @@ -373,7 +374,7 @@ async def __sync_lock( # Index Lock event await self.__insert_lock_idx( db_session=db_session, - transaction_hash=event["transactionHash"].hex(), + transaction_hash=event["transactionHash"].to_0x_hex(), msg_sender=msg_sender, block_number=event["blockNumber"], token_address=token.address, @@ -481,7 +482,7 @@ async def __sync_unlock( # Index Unlock event await self.__insert_unlock_idx( db_session=db_session, - transaction_hash=event["transactionHash"].hex(), + transaction_hash=event["transactionHash"].to_0x_hex(), msg_sender=msg_sender, block_number=event["blockNumber"], token_address=token.address, @@ -981,6 +982,131 @@ async def __sync_escrow( except Exception as e: raise e + async def __sync_dvp( + self, db_session: AsyncSession, block_from: int, block_to: int + ): + """Sync Events from IbetSecurityTokenDVP + + :param db_session: database session + :param block_from: From block + :param block_to: To block + :return: None + """ + for exchange_address in self.exchange_address_list: + try: + delivery = AsyncContractUtils.get_contract( + "IbetSecurityTokenDVP", exchange_address + ) + + account_list_tmp = [] + + # DeliveryCreated event + _event_list = await AsyncContractUtils.get_event_logs( + contract=delivery, + event="DeliveryCreated", + block_from=block_from, + block_to=block_to, + ) + for _event in _event_list: + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get( + "seller", ZERO_ADDRESS + ), # only seller has changed + } + ) + + # DeliveryCanceled event + _event_list = await AsyncContractUtils.get_event_logs( + contract=delivery, + event="DeliveryCanceled", + block_from=block_from, + block_to=block_to, + ) + for _event in _event_list: + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get( + "seller", ZERO_ADDRESS + ), # only seller has changed + } + ) + + # DeliveryAborted event + _event_list = await AsyncContractUtils.get_event_logs( + contract=delivery, + event="DeliveryAborted", + block_from=block_from, + block_to=block_to, + ) + for _event in _event_list: + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get( + "seller", ZERO_ADDRESS + ), # only seller has changed + } + ) + + # HolderChanged event + _event_list = await AsyncContractUtils.get_event_logs( + contract=delivery, + event="HolderChanged", + block_from=block_from, + block_to=block_to, + ) + for _event in _event_list: + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get("from", ZERO_ADDRESS), + } + ) + account_list_tmp.append( + { + "token_address": _event["args"].get("token", ZERO_ADDRESS), + "account_address": _event["args"].get("to", ZERO_ADDRESS), + } + ) + + # Make temporary list unique + account_list_tmp.sort( + key=lambda x: (x["token_address"], x["account_address"]) + ) + account_list = [] + for k, g in groupby( + account_list_tmp, + lambda x: (x["token_address"], x["account_address"]), + ): + account_list.append( + {"token_address": k[0], "account_address": k[1]} + ) + + # Update position + for _account in account_list: + token_address = _account["token_address"] + account_address = _account["account_address"] + ( + exchange_balance, + exchange_commitment, + ) = await self.__get_account_balance_exchange( + exchange_address=exchange_address, + token_address=token_address, + account_address=account_address, + ) + await self.__sink_on_position( + db_session=db_session, + token_address=token_address, + account_address=account_address, + exchange_balance=exchange_balance, + exchange_commitment=exchange_commitment, + ) + except Exception as e: + raise e + @staticmethod async def __insert_lock_idx( db_session: AsyncSession, diff --git a/batch/indexer_token_holders.py b/batch/indexer_token_holders.py index f64f22a1..a5e8f2ba 100644 --- a/batch/indexer_token_holders.py +++ b/batch/indexer_token_holders.py @@ -304,7 +304,7 @@ async def __process_transfer(self, block_from: int, block_to: int): { "event": _event["event"], "args": dict(_event["args"]), - "transaction_hash": _event["transactionHash"].hex(), + "transaction_hash": _event["transactionHash"].to_0x_hex(), "block_number": _event["blockNumber"], "log_index": _event["logIndex"], } @@ -322,7 +322,7 @@ async def __process_transfer(self, block_from: int, block_to: int): { "event": _event["event"], "args": dict(_event["args"]), - "transaction_hash": _event["transactionHash"].hex(), + "transaction_hash": _event["transactionHash"].to_0x_hex(), "block_number": _event["blockNumber"], "log_index": _event["logIndex"], } @@ -340,9 +340,9 @@ async def __process_transfer(self, block_from: int, block_to: int): amount = int(args.get("value")) # Skip sinking in case of deposit to exchange or withdrawal from exchange - if (await web3.eth.get_code(from_account)).hex() != "0x" or ( + if (await web3.eth.get_code(from_account)).to_0x_hex() != "0x" or ( await web3.eth.get_code(to_account) - ).hex() != "0x": + ).to_0x_hex() != "0x": continue if amount is not None and amount <= sys.maxsize: diff --git a/batch/indexer_transfer.py b/batch/indexer_transfer.py index d58f33bf..08313524 100644 --- a/batch/indexer_transfer.py +++ b/batch/indexer_transfer.py @@ -20,7 +20,7 @@ import asyncio import json import sys -from datetime import datetime +from datetime import UTC, datetime from typing import Sequence import uvloop @@ -201,10 +201,11 @@ async def __sync_transfer( ) for event in events: args = event["args"] - transaction_hash = event["transactionHash"].hex() - block_timestamp = datetime.utcfromtimestamp( - (await web3.eth.get_block(event["blockNumber"]))["timestamp"] - ) + transaction_hash = event["transactionHash"].to_0x_hex() + block_timestamp = datetime.fromtimestamp( + (await web3.eth.get_block(event["blockNumber"]))["timestamp"], + UTC, + ).replace(tzinfo=None) if args["value"] > sys.maxsize: pass else: @@ -242,10 +243,11 @@ async def __sync_unlock( ) for event in events: args = event["args"] - transaction_hash = event["transactionHash"].hex() - block_timestamp = datetime.utcfromtimestamp( - (await web3.eth.get_block(event["blockNumber"]))["timestamp"] - ) + transaction_hash = event["transactionHash"].to_0x_hex() + block_timestamp = datetime.fromtimestamp( + (await web3.eth.get_block(event["blockNumber"]))["timestamp"], + UTC, + ).replace(tzinfo=None) if args["value"] > sys.maxsize: pass else: diff --git a/batch/indexer_transfer_approval.py b/batch/indexer_transfer_approval.py index 2ef6ab62..d9098643 100644 --- a/batch/indexer_transfer_approval.py +++ b/batch/indexer_transfer_approval.py @@ -20,7 +20,7 @@ import asyncio import sys import uuid -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Optional, Sequence import uvloop @@ -284,7 +284,7 @@ async def __sync_token_apply_for_transfer( notice_code=0, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __sync_token_cancel_transfer( self, db_session: AsyncSession, block_from, block_to @@ -326,7 +326,7 @@ async def __sync_token_cancel_transfer( notice_code=1, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __sync_token_approve_transfer( self, db_session: AsyncSession, block_from, block_to @@ -369,7 +369,7 @@ async def __sync_token_approve_transfer( notice_code=2, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __sync_exchange_apply_for_transfer( self, db_session: AsyncSession, block_from, block_to @@ -417,7 +417,7 @@ async def __sync_exchange_apply_for_transfer( notice_code=0, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __sync_exchange_cancel_transfer( self, db_session: AsyncSession, block_from, block_to @@ -459,7 +459,7 @@ async def __sync_exchange_cancel_transfer( notice_code=1, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __sync_exchange_escrow_finished( self, db_session: AsyncSession, block_from: int, block_to: int @@ -500,7 +500,7 @@ async def __sync_exchange_escrow_finished( notice_code=3, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __sync_exchange_approve_transfer( self, db_session: AsyncSession, block_from: int, block_to: int @@ -541,7 +541,7 @@ async def __sync_exchange_approve_transfer( notice_code=2, ) except Exception: - LOG.exception("An exception occurred during event synchronization") + raise async def __register_notification( self, @@ -665,17 +665,17 @@ async def __sink_on_transfer_approval( transfer_approval.amount = amount try: transfer_approval.application_datetime = datetime.fromtimestamp( - float(optional_data_applicant), tz=timezone.utc + float(optional_data_applicant), tz=UTC ) except ValueError: transfer_approval.application_datetime = None transfer_approval.application_blocktimestamp = datetime.fromtimestamp( - block_timestamp, tz=timezone.utc + block_timestamp, tz=UTC ) elif event_type == "Cancel": if transfer_approval is not None: transfer_approval.cancellation_blocktimestamp = datetime.fromtimestamp( - block_timestamp, tz=timezone.utc + block_timestamp, tz=UTC ) transfer_approval.cancelled = True elif event_type == "EscrowFinish": @@ -685,12 +685,12 @@ async def __sink_on_transfer_approval( if transfer_approval is not None: try: transfer_approval.approval_datetime = datetime.fromtimestamp( - float(optional_data_approver), tz=timezone.utc + float(optional_data_approver), tz=UTC ) except ValueError: transfer_approval.approval_datetime = None transfer_approval.approval_blocktimestamp = datetime.fromtimestamp( - block_timestamp, tz=timezone.utc + block_timestamp, tz=UTC ) transfer_approval.transfer_approved = True await db_session.merge(transfer_approval) diff --git a/batch/processor_create_utxo.py b/batch/processor_create_utxo.py index c6d14bac..eaa4cd1d 100644 --- a/batch/processor_create_utxo.py +++ b/batch/processor_create_utxo.py @@ -20,7 +20,7 @@ import asyncio import sys import time -from datetime import datetime +from datetime import UTC, datetime from typing import Sequence import uvloop @@ -226,7 +226,7 @@ async def __process_transfer( { "event": _event["event"], "args": dict(_event["args"]), - "transaction_hash": _event["transactionHash"].hex(), + "transaction_hash": _event["transactionHash"].to_0x_hex(), "block_number": _event["blockNumber"], "log_index": _event["logIndex"], } @@ -244,7 +244,7 @@ async def __process_transfer( { "event": _event["event"], "args": dict(_event["args"]), - "transaction_hash": _event["transactionHash"].hex(), + "transaction_hash": _event["transactionHash"].to_0x_hex(), "block_number": _event["blockNumber"], "log_index": _event["logIndex"], } @@ -266,7 +266,7 @@ async def __process_transfer( { "event": _event["event"], "args": dict(_event["args"]), - "transaction_hash": _event["transactionHash"].hex(), + "transaction_hash": _event["transactionHash"].to_0x_hex(), "block_number": _event["blockNumber"], "log_index": _event["logIndex"], } @@ -291,15 +291,17 @@ async def __process_transfer( amount = int(args.get("value")) # Skip sinking in case of deposit to exchange or withdrawal from exchange - if (await web3.eth.get_code(from_account)).hex() != "0x" or ( + if (await web3.eth.get_code(from_account)).to_0x_hex() != "0x" or ( await web3.eth.get_code(to_account) - ).hex() != "0x": + ).to_0x_hex() != "0x": continue transaction_hash = event["transaction_hash"] block_number = event["block_number"] - block_timestamp = datetime.utcfromtimestamp( - (await web3.eth.get_block(block_number))["timestamp"] + block_timestamp = datetime.fromtimestamp( + (await web3.eth.get_block(block_number))["timestamp"], UTC + ).replace( + tzinfo=None ) # UTC if amount is not None and amount <= sys.maxsize: @@ -368,10 +370,12 @@ async def __process_issue( account = args.get("targetAddress", ZERO_ADDRESS) amount = args.get("amount") - transaction_hash = event["transactionHash"].hex() + transaction_hash = event["transactionHash"].to_0x_hex() block_number = event["blockNumber"] - block_timestamp = datetime.utcfromtimestamp( - (await web3.eth.get_block(block_number))["timestamp"] + block_timestamp = datetime.fromtimestamp( + (await web3.eth.get_block(block_number))["timestamp"], UTC + ).replace( + tzinfo=None ) # UTC if amount is not None and amount <= sys.maxsize: @@ -428,10 +432,12 @@ async def __process_redeem( account = args.get("targetAddress", ZERO_ADDRESS) amount = args.get("amount") - transaction_hash = event["transactionHash"].hex() + transaction_hash = event["transactionHash"].to_0x_hex() block_number = event["blockNumber"] - block_timestamp = datetime.utcfromtimestamp( - (await web3.eth.get_block(block_number))["timestamp"] + block_timestamp = datetime.fromtimestamp( + (await web3.eth.get_block(block_number))["timestamp"], UTC + ).replace( + tzinfo=None ) # UTC if amount is not None and amount <= sys.maxsize: diff --git a/batch/processor_monitor_block_sync.py b/batch/processor_monitor_block_sync.py index dfda20ac..a173c591 100644 --- a/batch/processor_monitor_block_sync.py +++ b/batch/processor_monitor_block_sync.py @@ -25,8 +25,8 @@ from sqlalchemy import delete, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.ext.asyncio import AsyncSession -from web3 import AsyncWeb3, Web3 -from web3.middleware import async_geth_poa_middleware +from web3 import AsyncHTTPProvider, AsyncWeb3, Web3 +from web3.middleware import ExtraDataToPOAMiddleware from app.database import BatchAsyncSessionLocal from app.exceptions import ServiceUnavailableError @@ -121,8 +121,8 @@ async def __set_node_info( ): self.node_info[endpoint_uri] = {"priority": priority} - web3 = AsyncWeb3(Web3.AsyncHTTPProvider(endpoint_uri)) - web3.middleware_onion.inject(async_geth_poa_middleware, layer=0) + web3 = AsyncWeb3(AsyncHTTPProvider(endpoint_uri)) + web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) self.node_info[endpoint_uri]["web3"] = web3 # Get block number diff --git a/batch/processor_rotate_e2e_messaging_rsa_key.py b/batch/processor_rotate_e2e_messaging_rsa_key.py index 653b794f..d30a2fd3 100644 --- a/batch/processor_rotate_e2e_messaging_rsa_key.py +++ b/batch/processor_rotate_e2e_messaging_rsa_key.py @@ -20,7 +20,7 @@ import asyncio import sys import time -from datetime import datetime +from datetime import UTC, datetime from typing import Sequence import uvloop @@ -176,9 +176,9 @@ async def __auto_generate_rsa_key( _account_rsa_key.rsa_private_key = rsa_private_key _account_rsa_key.rsa_public_key = rsa_public_key _account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(pass_phrase) - _account_rsa_key.block_timestamp = datetime.utcfromtimestamp( - block["timestamp"] - ) + _account_rsa_key.block_timestamp = datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) db_session.add(_account_rsa_key) @staticmethod diff --git a/batch/processor_scheduled_events.py b/batch/processor_scheduled_events.py index c2b1b036..5e5f9070 100644 --- a/batch/processor_scheduled_events.py +++ b/batch/processor_scheduled_events.py @@ -21,7 +21,7 @@ import sys import time import uuid -from datetime import datetime +from datetime import UTC, datetime, timezone from typing import List, Optional, Sequence, Set import uvloop @@ -78,7 +78,7 @@ def __init__(self, worker_num: int): async def process(self): db_session: AsyncSession = BatchAsyncSessionLocal() try: - process_start_time = datetime.utcnow() + process_start_time = datetime.now(UTC).replace(tzinfo=None) while True: events_list = await self.__get_events_of_one_issuer( db_session=db_session, filter_time=process_start_time diff --git a/batch/processor_update_token.py b/batch/processor_update_token.py index 881e7d8c..19518d2a 100644 --- a/batch/processor_update_token.py +++ b/batch/processor_update_token.py @@ -20,7 +20,7 @@ import asyncio import sys import uuid -from datetime import datetime +from datetime import UTC, datetime from typing import Sequence import uvloop @@ -211,9 +211,9 @@ async def process(self): _utxo.token_address = _update_token.token_address _utxo.amount = _update_token.arguments.get("total_supply") _utxo.block_number = block["number"] - _utxo.block_timestamp = datetime.utcfromtimestamp( - block["timestamp"] - ) + _utxo.block_timestamp = datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) db_session.add(_utxo) await self.__sink_on_finish_update_process( @@ -272,53 +272,57 @@ def __create_update_data(trigger, token_type, arguments: dict): if trigger == "Issue": # NOTE: Items set at the time of issue do not need to be updated. if token_type == TokenType.IBET_SHARE.value: - update_data = { - "tradable_exchange_contract_address": arguments.get( + return IbetShareUpdateParams( + tradable_exchange_contract_address=arguments.get( "tradable_exchange_contract_address" ), - "personal_info_contract_address": arguments.get( + personal_info_contract_address=arguments.get( "personal_info_contract_address" ), - "transferable": arguments.get("transferable"), - "status": arguments.get("status"), - "is_offering": arguments.get("is_offering"), - "contact_information": arguments.get("contact_information"), - "privacy_policy": arguments.get("privacy_policy"), - "transfer_approval_required": arguments.get( + require_personal_info_registered=arguments.get( + "require_personal_info_registered" + ), + transferable=arguments.get("transferable"), + status=arguments.get("status"), + is_offering=arguments.get("is_offering"), + contact_information=arguments.get("contact_information"), + privacy_policy=arguments.get("privacy_policy"), + transfer_approval_required=arguments.get( "transfer_approval_required" ), - "is_canceled": arguments.get("is_canceled"), - } - return IbetShareUpdateParams(**update_data) + is_canceled=arguments.get("is_canceled"), + ) elif token_type == TokenType.IBET_STRAIGHT_BOND.value: - update_data = { - "face_value_currency": arguments.get("face_value_currency"), - "redemption_value_currency": arguments.get( + return IbetStraightBondUpdateParams( + face_value_currency=arguments.get("face_value_currency"), + redemption_value_currency=arguments.get( "redemption_value_currency" ), - "interest_rate": arguments.get("interest_rate"), - "interest_payment_date": arguments.get("interest_payment_date"), - "interest_payment_currency": arguments.get( + interest_rate=arguments.get("interest_rate"), + interest_payment_date=arguments.get("interest_payment_date"), + interest_payment_currency=arguments.get( "interest_payment_currency" ), - "base_fx_rate": arguments.get("base_fx_rate"), - "transferable": arguments.get("transferable"), - "status": arguments.get("status"), - "is_offering": arguments.get("is_offering"), - "is_redeemed": arguments.get("is_redeemed"), - "tradable_exchange_contract_address": arguments.get( + base_fx_rate=arguments.get("base_fx_rate"), + transferable=arguments.get("transferable"), + status=arguments.get("status"), + is_offering=arguments.get("is_offering"), + is_redeemed=arguments.get("is_redeemed"), + tradable_exchange_contract_address=arguments.get( "tradable_exchange_contract_address" ), - "personal_info_contract_address": arguments.get( + personal_info_contract_address=arguments.get( "personal_info_contract_address" ), - "contact_information": arguments.get("contact_information"), - "privacy_policy": arguments.get("privacy_policy"), - "transfer_approval_required": arguments.get( + require_personal_info_registered=arguments.get( + "require_personal_info_registered" + ), + contact_information=arguments.get("contact_information"), + privacy_policy=arguments.get("privacy_policy"), + transfer_approval_required=arguments.get( "transfer_approval_required" ), - } - return IbetStraightBondUpdateParams(**update_data) + ) return @staticmethod diff --git a/bin/healthcheck_indexer.sh b/bin/healthcheck_indexer.sh index 6c193dbc..def0c669 100755 --- a/bin/healthcheck_indexer.sh +++ b/bin/healthcheck_indexer.sh @@ -25,6 +25,7 @@ PROC_LIST="${PROC_LIST} batch/indexer_transfer.py" PROC_LIST="${PROC_LIST} batch/indexer_transfer_approval.py" PROC_LIST="${PROC_LIST} batch/indexer_issue_redeem.py" PROC_LIST="${PROC_LIST} batch/indexer_token_cache.py" +PROC_LIST="${PROC_LIST} batch/indexer_delivery.py" if [ -n "${E2E_MESSAGING_CONTRACT_ADDRESS}" ]; then PROC_LIST="${PROC_LIST} batch/indexer_e2e_messaging.py" diff --git a/bin/run_indexer.sh b/bin/run_indexer.sh index 4ce334c5..4a3aa8a5 100755 --- a/bin/run_indexer.sh +++ b/bin/run_indexer.sh @@ -29,6 +29,7 @@ python batch/indexer_transfer.py & python batch/indexer_transfer_approval.py & python batch/indexer_issue_redeem.py & python batch/indexer_token_cache.py & +python batch/indexer_delivery.py & if [ -n "${E2E_MESSAGING_CONTRACT_ADDRESS}" ]; then python batch/indexer_e2e_messaging.py & diff --git a/cmd/explorer/poetry.lock b/cmd/explorer/poetry.lock index 625691d3..91b597d5 100644 --- a/cmd/explorer/poetry.lock +++ b/cmd/explorer/poetry.lock @@ -3,5 +3,5 @@ package = [] [metadata] lock-version = "2.0" -python-versions = "3.11.2" +python-versions = "3.12.2" content-hash = "5238f2940f85ee545d00dfdc196d3dc793fbad517b6ee1172c9644982f7fe101" diff --git a/cmd/explorer/pyproject.toml b/cmd/explorer/pyproject.toml index cdc2b5fb..52853d44 100644 --- a/cmd/explorer/pyproject.toml +++ b/cmd/explorer/pyproject.toml @@ -5,17 +5,17 @@ description = "ibet-Prime Terminal UI for Block Chain Explorer" authors = ["BOOSTRY Co., Ltd. "] readme = "README.md" packages = [ - { include = "src" }, + { include = "../explorer" }, ] [tool.poetry.dependencies] -python = "3.11.2" +python = "3.12.2" [tool.poetry.scripts] -ibet-explorer = "src.main:app" +ibet-explorer = "explorer.src.main:app" [tool.mypy] -python_version = "3.11" +python_version = "3.12" no_strict_optional = true ignore_missing_imports = true check_untyped_defs = true diff --git a/cmd/explorer/src/connector/__init__.py b/cmd/explorer/src/connector/__init__.py index eedcba83..4e197735 100644 --- a/cmd/explorer/src/connector/__init__.py +++ b/cmd/explorer/src/connector/__init__.py @@ -21,6 +21,7 @@ from typing import Any from aiohttp import ClientSession +from cache import AsyncTTL from app.model.schema import ( BlockDataDetail, @@ -31,7 +32,6 @@ TxDataDetail, TxDataListResponse, ) -from cache import AsyncTTL class ApiNotEnabledException(Exception): diff --git a/cmd/settlement/Makefile b/cmd/settlement/Makefile new file mode 100644 index 00000000..fe3aafb4 --- /dev/null +++ b/cmd/settlement/Makefile @@ -0,0 +1,18 @@ +.PHONY: isort black test run + +format: isort black + +isort: + isort src/. + +black: + poetry run black src + +test: + pytest . + +console: + textual console + +run: + poetry run python src/main.py \ No newline at end of file diff --git a/cmd/settlement/README.md b/cmd/settlement/README.md new file mode 100644 index 00000000..3fc61cde --- /dev/null +++ b/cmd/settlement/README.md @@ -0,0 +1,94 @@ +# ibet settlement CLI + +## Run + +### with container + +```bash +> docker exec -it -e "TERM=xterm-256color" ibet-prime-app bash --login +> apl@2e5a80e06fcb:/$ settlement-cli --help + + Usage: settlement-cli [OPTIONS] COMMAND [ARGS]... + +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --install-completion Install completion for the current shell. │ +│ --show-completion Show completion for the current shell, to copy it or customize the installation. │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Commands ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ abort │ +│ create_agent │ +│ finish │ +│ list │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` + +## Commands + +### List + +```bash + Usage: settlement-cli list [OPTIONS] EXCHANGE_ADDRESS AGENT_ADDRESS [STATUS]:[DELIVER + Y_CREATED|DELIVERY_CANCELED|DELIVERY_CONFIRMED|DELIVERY_FI + NISHED|DELIVERY_ABORTED] [API_URL] + +╭─ Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ * exchange_address TEXT [default: None] [required] │ +│ * agent_address TEXT [default: None] [required] │ +│ status [STATUS]:[DELIVERY_CREATED|DELIVERY_CANCELED|DELIVERY_CONFIRMED|DELIVERY_FINISHED|DELIVERY_ABORTED] [default: delivery_confirmed] │ +│ api_url [API_URL] [env var: API_URL] [default: http://localhost:5000] │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` + +### Finish + +```bash + Usage: settlement-cli finish [OPTIONS] EXCHANGE_ADDRESS AGENT_ADDRESS DELIVERY_ID + [EOA_PASSWORD] [API_URL] + +╭─ Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ * exchange_address TEXT [default: None] [required] │ +│ * agent_address TEXT [default: None] [required] │ +│ * delivery_id INTEGER [default: None] [required] │ +│ eoa_password [EOA_PASSWORD] [env var: EOA_PASSWORD] [default: None] │ +│ api_url [API_URL] [env var: API_URL] [default: http://localhost:5000] │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` + +### Abort + +```bash + Usage: settlement-cli abort [OPTIONS] EXCHANGE_ADDRESS AGENT_ADDRESS DELIVERY_ID + [EOA_PASSWORD] [API_URL] + +╭─ Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ * exchange_address TEXT [default: None] [required] │ +│ * agent_address TEXT [default: None] [required] │ +│ * delivery_id INTEGER [default: None] [required] │ +│ eoa_password [EOA_PASSWORD] [env var: EOA_PASSWORD] [default: None] │ +│ api_url [API_URL] [env var: API_URL] [default: http://localhost:5000] │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` + +### Create DVP Agent + +```bash + Usage: settlement-cli create_agent [OPTIONS] EOA_PASSWORD [API_URL] + +╭─ Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ * eoa_password TEXT [env var: EOA_PASSWORD] [default: None] [required] │ +│ api_url [API_URL] [env var: API_URL] [default: http://localhost:5000] │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` \ No newline at end of file diff --git a/cmd/settlement/pyproject.toml b/cmd/settlement/pyproject.toml new file mode 100644 index 00000000..082eaa67 --- /dev/null +++ b/cmd/settlement/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "ibet-prime-settlement" +version = "0.1.0" +description = "ibet-Prime Settlement CLI" +authors = ["BOOSTRY Co., Ltd. "] +readme = "README.md" +packages = [ + { include = "../settlement" }, +] + +[tool.poetry.dependencies] +python = "3.12.2" + +[tool.poetry.scripts] +settlement-cli = "settlement.src.main:app" + +[tool.mypy] +python_version = "3.12" +no_strict_optional = true +ignore_missing_imports = true +check_untyped_defs = true + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file diff --git a/cmd/settlement/src/__init__.py b/cmd/settlement/src/__init__.py new file mode 100644 index 00000000..5ebbd941 --- /dev/null +++ b/cmd/settlement/src/__init__.py @@ -0,0 +1,18 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" diff --git a/cmd/settlement/src/main.py b/cmd/settlement/src/main.py new file mode 100644 index 00000000..9430beff --- /dev/null +++ b/cmd/settlement/src/main.py @@ -0,0 +1,237 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import base64 +import json +import logging +import sys +from typing import Annotated + +import click +import httpx +import typer +from Crypto.Cipher import PKCS1_OAEP +from Crypto.PublicKey import RSA +from rich import print +from rich.console import Console +from rich.table import Table + +from app.model.db import DeliveryStatus +from app.model.schema import ( + AbortDVPDeliveryRequest, + CreateDVPAgentAccountRequest, + FinishDVPDeliveryRequest, +) + +logging.getLogger("httpx").setLevel(logging.WARNING) +logging.getLogger("httpcore").setLevel(logging.WARNING) + + +app = typer.Typer(pretty_exceptions_show_locals=False) + + +@app.command(name="list") +def list_deliveries( + exchange_address: str, + agent_address: str, + status: Annotated[ + str, + typer.Argument( + click_type=click.Choice( + [DeliveryStatus(d).name for d in DeliveryStatus], case_sensitive=False + ) + ), + ] = "delivery_confirmed", + api_url: Annotated[ + str, typer.Argument(..., envvar="API_URL") + ] = "http://localhost:5000", +): + resp = httpx.get( + url=f"{api_url}/settlement/dvp/{exchange_address}/deliveries", + params={ + "agent_address": agent_address, + "status": DeliveryStatus[status.upper()].value, + }, + ) + + if resp.status_code != 200: + typer.echo(typer.style("Failed to get deliveries", fg="red"), err=True) + print(resp.json()) + sys.exit(1) + + console = Console() + + delivery_table = Table() + delivery_table.add_column("Delivery ID") + delivery_table.add_column("Token Address") + delivery_table.add_column("Buyer") + delivery_table.add_column("Seller") + delivery_table.add_column("Amount") + delivery_table.add_column("Agent") + delivery_table.add_column("Data") + delivery_table.add_column("Status") + + for idx_delivery in resp.json()["deliveries"]: + match idx_delivery["status"]: + case DeliveryStatus.DELIVERY_CREATED: + status = "Created" + case DeliveryStatus.DELIVERY_CANCELED: + status = "Canceled" + case DeliveryStatus.DELIVERY_CONFIRMED: + status = "Confirmed" + case DeliveryStatus.DELIVERY_FINISHED: + status = "Finished" + case DeliveryStatus.DELIVERY_ABORTED: + status = "Aborted" + case _: + status = None + + delivery_table.add_row( + str(idx_delivery["delivery_id"]), + idx_delivery["token_address"], + idx_delivery["buyer_address"], + idx_delivery["seller_address"], + str(idx_delivery["amount"]), + idx_delivery["agent_address"], + idx_delivery["data"], + status, + ) + + console.print(delivery_table) + + +@app.command(name="finish") +def finish( + exchange_address: str, + agent_address: str, + delivery_id: int, + eoa_password: Annotated[str, typer.Argument(..., envvar="EOA_PASSWORD")] = None, + api_url: Annotated[ + str, typer.Argument(..., envvar="API_URL") + ] = "http://localhost:5000", +): + resp = httpx.get(url=f"{api_url}/e2ee") + + if resp.status_code != 200: + print("Failed to get e2ee pubkey") + print(resp.json()) + sys.exit(1) + + if resp.json()["public_key"] is not None: + rsa_publickey = RSA.importKey(resp.json()["public_key"]) + cipher = PKCS1_OAEP.new(rsa_publickey) + eoa_password = base64.encodebytes(cipher.encrypt(eoa_password.encode("utf-8"))) + + finish_params = FinishDVPDeliveryRequest( + operation_type="Finish", + account_address=agent_address, + eoa_password=eoa_password, + ) + + resp = httpx.post( + url=f"{api_url}/settlement/dvp/{exchange_address}/delivery/{delivery_id}", + json=json.loads(finish_params.model_dump_json()), + ) + + if resp.status_code != 200: + typer.echo(typer.style("Failed to finish the delivery", fg="red"), err=True) + print(resp.json()) + sys.exit(1) + + typer.echo(typer.style("Successfully finished the delivery", fg="blue")) + print(f"delivery_id: {delivery_id}") + + +@app.command(name="abort") +def abort( + exchange_address: str, + agent_address: str, + delivery_id: int, + eoa_password: Annotated[str, typer.Argument(..., envvar="EOA_PASSWORD")] = None, + api_url: Annotated[ + str, typer.Argument(..., envvar="API_URL") + ] = "http://localhost:5000", +): + resp = httpx.get(url=f"{api_url}/e2ee") + + if resp.status_code != 200: + print("Failed to get e2ee pubkey") + print(resp.json()) + sys.exit(1) + + if resp.json()["public_key"] is not None: + rsa_publickey = RSA.importKey(resp.json()["public_key"]) + cipher = PKCS1_OAEP.new(rsa_publickey) + eoa_password = base64.encodebytes(cipher.encrypt(eoa_password.encode("utf-8"))) + + abort_params = AbortDVPDeliveryRequest( + operation_type="Abort", account_address=agent_address, eoa_password=eoa_password + ) + + resp = httpx.post( + url=f"{api_url}/settlement/dvp/{exchange_address}/delivery/{delivery_id}", + json=json.loads(abort_params.model_dump_json()), + ) + + if resp.status_code != 200: + typer.echo(typer.style("Failed to abort the delivery", fg="red"), err=True) + print(resp.json()) + sys.exit(1) + + typer.echo(typer.style("Successfully aborted the delivery", fg="blue")) + print(f"delivery_id: {delivery_id}") + + +@app.command(name="create_agent") +def create_agent( + eoa_password: Annotated[str, typer.Argument(..., envvar="EOA_PASSWORD")], + api_url: Annotated[ + str, typer.Argument(..., envvar="API_URL") + ] = "http://localhost:5000", +): + resp = httpx.get(url=f"{api_url}/e2ee") + + if resp.status_code != 200: + print("Failed to get e2ee pubkey") + print(resp.json()) + sys.exit(1) + + if resp.json()["public_key"] is not None: + rsa_publickey = RSA.importKey(resp.json()["public_key"]) + cipher = PKCS1_OAEP.new(rsa_publickey) + eoa_password = base64.encodebytes(cipher.encrypt(eoa_password.encode("utf-8"))) + + create_agent_param = CreateDVPAgentAccountRequest(eoa_password=eoa_password) + + resp = httpx.post( + url=f"{api_url}/settlement/dvp/agent/accounts", + json=json.loads(create_agent_param.model_dump_json()), + ) + + if resp.status_code != 200: + typer.echo(typer.style("Failed to create agent account", fg="red"), err=True) + print(resp.json()) + sys.exit(1) + + typer.echo(typer.style("Successfully created agent account", fg="blue")) + print(f"account_address: {resp.json()['account_address']}") + + +if __name__ == "__main__": + app() diff --git a/contracts/DVPStorage.json b/contracts/DVPStorage.json new file mode 100644 index 00000000..62e7d3ed --- /dev/null +++ b/contracts/DVPStorage.json @@ -0,0 +1,335 @@ +{ + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "commitments", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "getBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "getCommitment", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deliveryId", + "type": "uint256" + } + ], + "name": "getDelivery", + "outputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "agent", + "type": "address" + }, + { + "internalType": "bool", + "name": "confirmed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "valid", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLatestDeliveryId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestVersion", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "setBalance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "setCommitment", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deliveryId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_seller", + "type": "address" + }, + { + "internalType": "address", + "name": "_buyer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_agent", + "type": "address" + }, + { + "internalType": "bool", + "name": "_confirmed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "_valid", + "type": "bool" + } + ], + "name": "setDelivery", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_latestDeliveryId", + "type": "uint256" + } + ], + "name": "setLatestDeliveryId", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newVersion", + "type": "address" + } + ], + "name": "upgradeVersion", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "6080604052600060055534801561001557600080fd5b50600080546001600160a01b0319163317905561086b806100376000396000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063d04c23af1161008c578063de8211a911610066578063de8211a914610215578063ecf89597146102c3578063f2fde38b146102d6578063fd44b92e146102e957600080fd5b8063d04c23af146101b4578063d28eb963146101c7578063d4fac45d146101dc57600080fd5b8063097d9012146100d45780638da5cb5b1461012057806399933b331461014b578063adb6f63f14610153578063b5c9cbbe1461017e578063c07f47d4146101a1575b600080fd5b61010d6100e23660046106a3565b6001600160a01b03918216600090815260036020908152604080832093909416825291909152205490565b6040519081526020015b60405180910390f35b600054610133906001600160a01b031681565b6040516001600160a01b039091168152602001610117565b60055461010d565b61010d6101613660046106a3565b600360209081526000928352604080842090915290825290205481565b61019161018c3660046106d6565b6102fc565b6040519015158152602001610117565b600154610133906001600160a01b031681565b6101916101c23660046106d6565b610381565b6101da6101d5366004610712565b6103fd565b005b61010d6101ea3660046106a3565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b610278610223366004610734565b6000908152600460208190526040909120805460018201546002830154600384015493909401546001600160a01b0392831695918316948316939281169160ff600160a01b8304811692600160a81b90041690565b604080516001600160a01b0398891681529688166020880152948716948601949094526060850192909252909316608083015291151560a082015290151560c082015260e001610117565b6101da6102d136600461075d565b610469565b6101da6102e4366004610712565b61054e565b6101da6102f7366004610734565b610638565b60015460408051808201909152600681526532353030303160d01b60208201526000916001600160a01b031633146103505760405162461bcd60e51b815260040161034791906107e6565b60405180910390fd5b50506001600160a01b0392831660009081526002602090815260408083209490951682529290925291902055600190565b60015460408051808201909152600681526532353030303160d01b60208201526000916001600160a01b031633146103cc5760405162461bcd60e51b815260040161034791906107e6565b50506001600160a01b0392831660009081526003602090815260408083209490951682529290925291902055600190565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146104465760405162461bcd60e51b815260040161034791906107e6565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b60015460408051808201909152600681526532353030303160d01b6020820152906001600160a01b031633146104b25760405162461bcd60e51b815260040161034791906107e6565b50600097885260046020819052604090982080546001600160a01b039889166001600160a01b0319918216178255600182018054988a169882169890981790975560028101805496891696909716959095179095556003840192909255919094018054921515600160a81b0260ff60a81b19951515600160a01b026001600160a81b031990941692909416919091179190911792909216179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146105975760405162461bcd60e51b815260040161034791906107e6565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b0382166105dc5760405162461bcd60e51b815260040161034791906107e6565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60015460408051808201909152600681526532353030303160d01b6020820152906001600160a01b031633146106815760405162461bcd60e51b815260040161034791906107e6565b50600555565b80356001600160a01b038116811461069e57600080fd5b919050565b600080604083850312156106b657600080fd5b6106bf83610687565b91506106cd60208401610687565b90509250929050565b6000806000606084860312156106eb57600080fd5b6106f484610687565b925061070260208501610687565b9150604084013590509250925092565b60006020828403121561072457600080fd5b61072d82610687565b9392505050565b60006020828403121561074657600080fd5b5035919050565b8035801515811461069e57600080fd5b600080600080600080600080610100898b03121561077a57600080fd5b8835975061078a60208a01610687565b965061079860408a01610687565b95506107a660608a01610687565b9450608089013593506107bb60a08a01610687565b92506107c960c08a0161074d565b91506107d760e08a0161074d565b90509295985092959890939650565b60006020808352835180602085015260005b81811015610814578581018301518582016040015282016107f8565b506000604082860101526040601f19601f830116850101925050509291505056fea26469706673582212204889423730a418bf1a26edf713979d543ae8c11a38568eeecfa7ddf5920b0f9664736f6c63430008170033", + "deployedBytecode": "608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063d04c23af1161008c578063de8211a911610066578063de8211a914610215578063ecf89597146102c3578063f2fde38b146102d6578063fd44b92e146102e957600080fd5b8063d04c23af146101b4578063d28eb963146101c7578063d4fac45d146101dc57600080fd5b8063097d9012146100d45780638da5cb5b1461012057806399933b331461014b578063adb6f63f14610153578063b5c9cbbe1461017e578063c07f47d4146101a1575b600080fd5b61010d6100e23660046106a3565b6001600160a01b03918216600090815260036020908152604080832093909416825291909152205490565b6040519081526020015b60405180910390f35b600054610133906001600160a01b031681565b6040516001600160a01b039091168152602001610117565b60055461010d565b61010d6101613660046106a3565b600360209081526000928352604080842090915290825290205481565b61019161018c3660046106d6565b6102fc565b6040519015158152602001610117565b600154610133906001600160a01b031681565b6101916101c23660046106d6565b610381565b6101da6101d5366004610712565b6103fd565b005b61010d6101ea3660046106a3565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b610278610223366004610734565b6000908152600460208190526040909120805460018201546002830154600384015493909401546001600160a01b0392831695918316948316939281169160ff600160a01b8304811692600160a81b90041690565b604080516001600160a01b0398891681529688166020880152948716948601949094526060850192909252909316608083015291151560a082015290151560c082015260e001610117565b6101da6102d136600461075d565b610469565b6101da6102e4366004610712565b61054e565b6101da6102f7366004610734565b610638565b60015460408051808201909152600681526532353030303160d01b60208201526000916001600160a01b031633146103505760405162461bcd60e51b815260040161034791906107e6565b60405180910390fd5b50506001600160a01b0392831660009081526002602090815260408083209490951682529290925291902055600190565b60015460408051808201909152600681526532353030303160d01b60208201526000916001600160a01b031633146103cc5760405162461bcd60e51b815260040161034791906107e6565b50506001600160a01b0392831660009081526003602090815260408083209490951682529290925291902055600190565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146104465760405162461bcd60e51b815260040161034791906107e6565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b60015460408051808201909152600681526532353030303160d01b6020820152906001600160a01b031633146104b25760405162461bcd60e51b815260040161034791906107e6565b50600097885260046020819052604090982080546001600160a01b039889166001600160a01b0319918216178255600182018054988a169882169890981790975560028101805496891696909716959095179095556003840192909255919094018054921515600160a81b0260ff60a81b19951515600160a01b026001600160a81b031990941692909416919091179190911792909216179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146105975760405162461bcd60e51b815260040161034791906107e6565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b0382166105dc5760405162461bcd60e51b815260040161034791906107e6565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60015460408051808201909152600681526532353030303160d01b6020820152906001600160a01b031633146106815760405162461bcd60e51b815260040161034791906107e6565b50600555565b80356001600160a01b038116811461069e57600080fd5b919050565b600080604083850312156106b657600080fd5b6106bf83610687565b91506106cd60208401610687565b90509250929050565b6000806000606084860312156106eb57600080fd5b6106f484610687565b925061070260208501610687565b9150604084013590509250925092565b60006020828403121561072457600080fd5b61072d82610687565b9392505050565b60006020828403121561074657600080fd5b5035919050565b8035801515811461069e57600080fd5b600080600080600080600080610100898b03121561077a57600080fd5b8835975061078a60208a01610687565b965061079860408a01610687565b95506107a660608a01610687565b9450608089013593506107bb60a08a01610687565b92506107c960c08a0161074d565b91506107d760e08a0161074d565b90509295985092959890939650565b60006020808352835180602085015260005b81811015610814578581018301518582016040015282016107f8565b506000604082860101526040601f19601f830116850101925050509291505056fea26469706673582212204889423730a418bf1a26edf713979d543ae8c11a38568eeecfa7ddf5920b0f9664736f6c63430008170033" +} \ No newline at end of file diff --git a/contracts/IbetSecurityTokenDVP.json b/contracts/IbetSecurityTokenDVP.json new file mode 100644 index 00000000..5f835cbf --- /dev/null +++ b/contracts/IbetSecurityTokenDVP.json @@ -0,0 +1,651 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_storageAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "deliveryId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "agent", + "type": "address" + } + ], + "name": "DeliveryAborted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "deliveryId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "agent", + "type": "address" + } + ], + "name": "DeliveryCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "deliveryId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "agent", + "type": "address" + } + ], + "name": "DeliveryConfirmed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "deliveryId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "agent", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "data", + "type": "string" + } + ], + "name": "DeliveryCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "deliveryId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "agent", + "type": "address" + } + ], + "name": "DeliveryFinished", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Deposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "HolderChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Withdrawn", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deliveryId", + "type": "uint256" + } + ], + "name": "abortDelivery", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_deliveryIdList", + "type": "uint256[]" + } + ], + "name": "bulkFinishDelivery", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deliveryId", + "type": "uint256" + } + ], + "name": "cancelDelivery", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "commitmentOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deliveryId", + "type": "uint256" + } + ], + "name": "confirmDelivery", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_buyer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_agent", + "type": "address" + }, + { + "internalType": "string", + "name": "_data", + "type": "string" + } + ], + "name": "createDelivery", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deliveryId", + "type": "uint256" + } + ], + "name": "finishDelivery", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deliveryId", + "type": "uint256" + } + ], + "name": "getDelivery", + "outputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "seller", + "type": "address" + }, + { + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "agent", + "type": "address" + }, + { + "internalType": "bool", + "name": "confirmed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "valid", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestDeliveryId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "storageAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "tokenFallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "60806040523480156200001157600080fd5b506040516200257f3803806200257f833981016040819052620000349162000068565b60008054336001600160a01b031991821617909155600180549091166001600160a01b03929092169190911790556200009a565b6000602082840312156200007b57600080fd5b81516001600160a01b03811681146200009357600080fd5b9392505050565b6124d580620000aa6000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c8063b62549af11610097578063de8211a911610066578063de8211a9146101fd578063f2fde38b1461025b578063f7888aec1461026e578063fd84cb971461028157600080fd5b8063b62549af146101af578063c0a36f8a146101c2578063c0ee0b8a146101d5578063da5f7c45146101ea57600080fd5b806385aa92a7116100d357806385aa92a71461014b5780638da5cb5b14610176578063a2ee704314610189578063b4d904761461019c57600080fd5b806309326794146100fa57806351cff8d9146101155780636a4c76f814610138575b600080fd5b610102610294565b6040519081526020015b60405180910390f35b610128610123366004611fcc565b610307565b604051901515815260200161010c565b610128610146366004611ff0565b61048b565b60015461015e906001600160a01b031681565b6040516001600160a01b03909116815260200161010c565b60005461015e906001600160a01b031681565b610102610197366004612009565b610a9a565b6101286101aa366004611ff0565b610b1b565b6101286101bd366004611ff0565b610f94565b6101026101d03660046120ce565b6113e0565b6101e86101e3366004612161565b611854565b005b6101286101f83660046121ce565b611912565b61021061020b366004611ff0565b611950565b604080516001600160a01b0398891681529688166020880152948716948601949094526060850192909252909316608083015291151560a082015290151560c082015260e00161010c565b6101e8610269366004611fcc565b6119e1565b61010261027c366004612009565b611acb565b61012861028f366004611ff0565b611b06565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa1580156102de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103029190612243565b905090565b6000806103143384611acb565b9050600081116040518060400160405280600681526020016532363035303160d01b815250906103605760405162461bcd60e51b815260040161035791906122a2565b60405180910390fd5b5060405163a9059cbb60e01b8152336004820152602481018290526001600160a01b0384169063a9059cbb906044016020604051808303816000875af11580156103ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d291906122ca565b50600154604051635ae4e5df60e11b81526001600160a01b039091169063b5c9cbbe9061040890339087906000906004016122e5565b6020604051808303816000875af1158015610427573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061044b91906122ca565b5060405133906001600160a01b038516907f85b9d5ac4b95740dafd1b47130d38b5f34a9104dd14b1c88a2fb5a44c3a00ce790600090a350600192915050565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa1580156104d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f99190612243565b8211156040518060400160405280600681526020016532363033303160d01b815250906105395760405162461bcd60e51b815260040161035791906122a2565b50610542611f78565b60015460405163de8211a960e01b8152600481018590526001600160a01b039091169063de8211a99060240160e060405180830381865afa15801561058b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105af9190612309565b151560c0880181905290151560a08801526001600160a01b039182166080880152606087019290925291821660408087019190915292821660208087019190915293909116845281518083019092526006825265191b1819981960d11b92820192909252906001146106345760405162461bcd60e51b815260040161035791906122a2565b5060a081015160408051808201909152600681526532363033303360d01b602082015290151560011461067a5760405162461bcd60e51b815260040161035791906122a2565b5060808101516040805180820190915260068152650c8d8c0ccc0d60d21b6020820152906001600160a01b031633146106c65760405162461bcd60e51b815260040161035791906122a2565b5080600001516001600160a01b031663200d2ed26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610709573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061072d91906122ca565b60408051808201909152600681526532363033303560d01b602082015290151560011461076d5760405162461bcd60e51b815260040161035791906122a2565b5080600001516001600160a01b0316636f3b993f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d491906122ca565b604080518082019091526006815265191b1819981b60d11b602082015290156108105760405162461bcd60e51b815260040161035791906122a2565b506001546040820151825160608401516001600160a01b039093169263b5c9cbbe929190610848906108428484611acb565b90611f60565b6040518463ffffffff1660e01b8152600401610866939291906122e5565b6020604051808303816000875af1158015610885573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a991906122ca565b506001546020820151825160608401516001600160a01b039093169263d04c23af9291906108e1906108db8484610a9a565b90611f6c565b6040518463ffffffff1660e01b81526004016108ff939291906122e5565b6020604051808303816000875af115801561091e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094291906122ca565b50600154815160208301516040808501516060860151608087015160a0880151935163ecf8959760e01b81526001600160a01b039097169663ecf895979661099c968c969195909490939092909190600090600401612393565b600060405180830381600087803b1580156109b657600080fd5b505af11580156109ca573d6000803e3d6000fd5b5050505080600001516001600160a01b0316837fae13fd5bb7db89e0f8059ff395d808a71fc16b9ea25a1e8be803f1419575902e8360200151846040015185606001518660800151604051610a2294939291906123dc565b60405180910390a380604001516001600160a01b031681602001516001600160a01b031682600001516001600160a01b03167f2458504403156ef04185b3cb0aab362f0421374a9fccb513fcf87c416324d40e8460600151604051610a8991815260200190565b60405180910390a450600192915050565b6001546040516304bec80960e11b81526001600160a01b0384811660048301528381166024830152600092169063097d9012906044015b602060405180830381865afa158015610aee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b129190612243565b90505b92915050565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa158015610b65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b899190612243565b8211156040518060400160405280600681526020016532363031303160d01b81525090610bc95760405162461bcd60e51b815260040161035791906122a2565b50610bd2611f78565b60015460405163de8211a960e01b8152600481018590526001600160a01b039091169063de8211a99060240160e060405180830381865afa158015610c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3f9190612309565b151560c0880181905290151560a08801526001600160a01b039182166080880152606087019290925291821660408087019190915292821660208087019190915293909116845281518083019092526006825265191b1818981960d11b9282019290925290600114610cc45760405162461bcd60e51b815260040161035791906122a2565b5060a081015160408051808201909152600681526532363031303360d01b60208201529015610d065760405162461bcd60e51b815260040161035791906122a2565b5080602001516001600160a01b0316336001600160a01b03161480610d40575080604001516001600160a01b0316336001600160a01b0316145b604051806040016040528060068152602001650c8d8c0c4c0d60d21b81525090610d7d5760405162461bcd60e51b815260040161035791906122a2565b506001546020820151825160608401516001600160a01b039093169263b5c9cbbe929190610daf906108428484611acb565b6040518463ffffffff1660e01b8152600401610dcd939291906122e5565b6020604051808303816000875af1158015610dec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1091906122ca565b506001546020820151825160608401516001600160a01b039093169263d04c23af929190610e42906108db8484610a9a565b6040518463ffffffff1660e01b8152600401610e60939291906122e5565b6020604051808303816000875af1158015610e7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea391906122ca565b50600154815160208301516040808501516060860151608087015160a0880151935163ecf8959760e01b81526001600160a01b039097169663ecf8959796610efd968c969195909490939092909190600090600401612393565b600060405180830381600087803b158015610f1757600080fd5b505af1158015610f2b573d6000803e3d6000fd5b5050505080600001516001600160a01b0316837f83f6050bab67e325a246d7c944af50a51620253dcae7243de09efa23b2fb52238360200151846040015185606001518660800151604051610f8394939291906123dc565b60405180910390a350600192915050565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa158015610fde573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110029190612243565b8211156040518060400160405280600681526020016532363034303160d01b815250906110425760405162461bcd60e51b815260040161035791906122a2565b5061104b611f78565b60015460405163de8211a960e01b8152600481018590526001600160a01b039091169063de8211a99060240160e060405180830381865afa158015611094573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b89190612309565b151560c0880181905290151560a08801526001600160a01b039182166080880152606087019290925291821660408087019190915292821660208087019190915293909116845281518083019092526006825265191b181a181960d11b928201929092529060011461113d5760405162461bcd60e51b815260040161035791906122a2565b5060a081015160408051808201909152600681526532363034303360d01b60208201529015156001146111835760405162461bcd60e51b815260040161035791906122a2565b5080608001516001600160a01b0316336001600160a01b031614604051806040016040528060068152602001650c8d8c0d0c0d60d21b815250906111da5760405162461bcd60e51b815260040161035791906122a2565b506001546020820151825160608401516001600160a01b039093169263b5c9cbbe92919061120c906108428484611acb565b6040518463ffffffff1660e01b815260040161122a939291906122e5565b6020604051808303816000875af1158015611249573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126d91906122ca565b506001546020820151825160608401516001600160a01b039093169263d04c23af92919061129f906108db8484610a9a565b6040518463ffffffff1660e01b81526004016112bd939291906122e5565b6020604051808303816000875af11580156112dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130091906122ca565b50600154815160208301516040808501516060860151608087015160a0880151935163ecf8959760e01b81526001600160a01b039097169663ecf895979661135a968c969195909490939092909190600090600401612393565b600060405180830381600087803b15801561137457600080fd5b505af1158015611388573d6000803e3d6000fd5b5050505080600001516001600160a01b0316837f37c971bd668c8d1b347a40a1db0d7338d910adcf4d93dc7f8a95b468ddda58828360200151846040015185606001518660800151604051610f8394939291906123dc565b60008084116040518060400160405280600681526020016532363030303160d01b815250906114225760405162461bcd60e51b815260040161035791906122a2565b508361142e3388611acb565b101560405180604001604052806006815260200165191b1818181960d11b8152509061146d5760405162461bcd60e51b815260040161035791906122a2565b50856001600160a01b031663200d2ed26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d091906122ca565b60408051808201909152600681526532363030303360d01b60208201529015156001146115105760405162461bcd60e51b815260040161035791906122a2565b50856001600160a01b0316636f3b993f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561154f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157391906122ca565b6040805180820190915260068152650c8d8c0c0c0d60d21b602082015290156115af5760405162461bcd60e51b815260040161035791906122a2565b50600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa1580156115fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161e9190612243565b61162990600161241d565b600154604051637ea25c9760e11b8152600481018390529192506001600160a01b03169063fd44b92e90602401600060405180830381600087803b15801561167057600080fd5b505af1158015611684573d6000803e3d6000fd5b50506001805460405163ecf8959760e01b81526001600160a01b03909116935063ecf8959792506116c69185918c9133918d918d918d91600091600401612393565b600060405180830381600087803b1580156116e057600080fd5b505af11580156116f4573d6000803e3d6000fd5b50506001546001600160a01b0316915063b5c9cbbe9050338961171b896108db8484611acb565b6040518463ffffffff1660e01b8152600401611739939291906122e5565b6020604051808303816000875af1158015611758573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177c91906122ca565b506001546001600160a01b031663d04c23af338961179e896108428484610a9a565b6040518463ffffffff1660e01b81526004016117bc939291906122e5565b6020604051808303816000875af11580156117db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ff91906122ca565b50866001600160a01b0316817f4a2cf5c0983fc0948d3f0751b04bdf091d03d7a1a5d757d1c93b5a8f2f295f413389898989604051611842959493929190612430565b60405180910390a39695505050505050565b6001546001600160a01b031663b5c9cbbe8433611875866108428484611acb565b6040518463ffffffff1660e01b8152600401611893939291906122e5565b6020604051808303816000875af11580156118b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d691906122ca565b506040516001600160a01b0384169033907f76dacbd9b11b7bbe3372e54b4adc1eeb5724ff92f91142a1a45722c28582ebfd90600090a3505050565b6000805b828110156119495761193f84848381811061193357611933612476565b9050602002013561048b565b9150600101611916565b5092915050565b60015460405163de8211a960e01b8152600481018390526000918291829182918291829182916001600160a01b03169063de8211a99060240160e060405180830381865afa1580156119a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ca9190612309565b959e949d50929b5090995097509550909350915050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611a2a5760405162461bcd60e51b815260040161035791906122a2565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b038216611a6f5760405162461bcd60e51b815260040161035791906122a2565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60015460405163d4fac45d60e01b81526001600160a01b0384811660048301528381166024830152600092169063d4fac45d90604401610ad1565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa158015611b50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b749190612243565b8211156040518060400160405280600681526020016532363032303160d01b81525090611bb45760405162461bcd60e51b815260040161035791906122a2565b50611bbd611f78565b60015460405163de8211a960e01b8152600481018590526001600160a01b039091169063de8211a99060240160e060405180830381865afa158015611c06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c2a9190612309565b151560c0880181905290151560a08801526001600160a01b039182166080880152606087019290925291821660408087019190915292821660208087019190915293909116845281518083019092526006825265191b1819181960d11b9282019290925290600114611caf5760405162461bcd60e51b815260040161035791906122a2565b5060a081015160408051808201909152600681526532363032303360d01b60208201529015611cf15760405162461bcd60e51b815260040161035791906122a2565b50604081810151815180830190925260068252650c8d8c0c8c0d60d21b60208301526001600160a01b03163314611d3b5760405162461bcd60e51b815260040161035791906122a2565b5080600001516001600160a01b031663200d2ed26040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da291906122ca565b60408051808201909152600681526532363032303560d01b6020820152901515600114611de25760405162461bcd60e51b815260040161035791906122a2565b5080600001516001600160a01b0316636f3b993f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e4991906122ca565b604080518082019091526006815265191b1819181b60d11b60208201529015611e855760405162461bcd60e51b815260040161035791906122a2565b5060018054825160208401516040808601516060870151608088015160c0890151935163ecf8959760e01b81526001600160a01b039097169763ecf8959797611eda978d979096909594939291600401612393565b600060405180830381600087803b158015611ef457600080fd5b505af1158015611f08573d6000803e3d6000fd5b5050505080600001516001600160a01b0316837ffa1e4d4f57c605169fce32ba32e2fd705e590b8337ad5a79c677440230e4482d8360200151846040015185606001518660800151604051610f8394939291906123dc565b6000610b12828461241d565b6000610b12828461248c565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6001600160a01b0381168114611fc957600080fd5b50565b600060208284031215611fde57600080fd5b8135611fe981611fb4565b9392505050565b60006020828403121561200257600080fd5b5035919050565b6000806040838503121561201c57600080fd5b823561202781611fb4565b9150602083013561203781611fb4565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff8084111561207357612073612042565b604051601f8501601f19908116603f0116810190828211818310171561209b5761209b612042565b816040528093508581528686860111156120b457600080fd5b858560208301376000602087830101525050509392505050565b600080600080600060a086880312156120e657600080fd5b85356120f181611fb4565b9450602086013561210181611fb4565b935060408601359250606086013561211881611fb4565b9150608086013567ffffffffffffffff81111561213457600080fd5b8601601f8101881361214557600080fd5b61215488823560208401612058565b9150509295509295909350565b60008060006060848603121561217657600080fd5b833561218181611fb4565b925060208401359150604084013567ffffffffffffffff8111156121a457600080fd5b8401601f810186136121b557600080fd5b6121c486823560208401612058565b9150509250925092565b600080602083850312156121e157600080fd5b823567ffffffffffffffff808211156121f957600080fd5b818501915085601f83011261220d57600080fd5b81358181111561221c57600080fd5b8660208260051b850101111561223157600080fd5b60209290920196919550909350505050565b60006020828403121561225557600080fd5b5051919050565b6000815180845260005b8181101561228257602081850181015186830182015201612266565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610b12602083018461225c565b805180151581146122c557600080fd5b919050565b6000602082840312156122dc57600080fd5b610b12826122b5565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600080600080600080600060e0888a03121561232457600080fd5b875161232f81611fb4565b602089015190975061234081611fb4565b604089015190965061235181611fb4565b606089015160808a0151919650945061236981611fb4565b925061237760a089016122b5565b915061238560c089016122b5565b905092959891949750929550565b9788526001600160a01b03968716602089015294861660408801529285166060870152608086019190915290921660a084015290151560c0830152151560e08201526101000190565b6001600160a01b03948516815292841660208401526040830191909152909116606082015260800190565b634e487b7160e01b600052601160045260246000fd5b80820180821115610b1557610b15612407565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a06080820181905260009061246b9083018461225c565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b81810381811115610b1557610b1561240756fea264697066735822122057d989c84f5a6d77f74e97300e91e6e78cca2d3270dca8553354c5bf221ba45064736f6c63430008170033", + "deployedBytecode": "608060405234801561001057600080fd5b50600436106100f55760003560e01c8063b62549af11610097578063de8211a911610066578063de8211a9146101fd578063f2fde38b1461025b578063f7888aec1461026e578063fd84cb971461028157600080fd5b8063b62549af146101af578063c0a36f8a146101c2578063c0ee0b8a146101d5578063da5f7c45146101ea57600080fd5b806385aa92a7116100d357806385aa92a71461014b5780638da5cb5b14610176578063a2ee704314610189578063b4d904761461019c57600080fd5b806309326794146100fa57806351cff8d9146101155780636a4c76f814610138575b600080fd5b610102610294565b6040519081526020015b60405180910390f35b610128610123366004611fcc565b610307565b604051901515815260200161010c565b610128610146366004611ff0565b61048b565b60015461015e906001600160a01b031681565b6040516001600160a01b03909116815260200161010c565b60005461015e906001600160a01b031681565b610102610197366004612009565b610a9a565b6101286101aa366004611ff0565b610b1b565b6101286101bd366004611ff0565b610f94565b6101026101d03660046120ce565b6113e0565b6101e86101e3366004612161565b611854565b005b6101286101f83660046121ce565b611912565b61021061020b366004611ff0565b611950565b604080516001600160a01b0398891681529688166020880152948716948601949094526060850192909252909316608083015291151560a082015290151560c082015260e00161010c565b6101e8610269366004611fcc565b6119e1565b61010261027c366004612009565b611acb565b61012861028f366004611ff0565b611b06565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa1580156102de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103029190612243565b905090565b6000806103143384611acb565b9050600081116040518060400160405280600681526020016532363035303160d01b815250906103605760405162461bcd60e51b815260040161035791906122a2565b60405180910390fd5b5060405163a9059cbb60e01b8152336004820152602481018290526001600160a01b0384169063a9059cbb906044016020604051808303816000875af11580156103ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d291906122ca565b50600154604051635ae4e5df60e11b81526001600160a01b039091169063b5c9cbbe9061040890339087906000906004016122e5565b6020604051808303816000875af1158015610427573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061044b91906122ca565b5060405133906001600160a01b038516907f85b9d5ac4b95740dafd1b47130d38b5f34a9104dd14b1c88a2fb5a44c3a00ce790600090a350600192915050565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa1580156104d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f99190612243565b8211156040518060400160405280600681526020016532363033303160d01b815250906105395760405162461bcd60e51b815260040161035791906122a2565b50610542611f78565b60015460405163de8211a960e01b8152600481018590526001600160a01b039091169063de8211a99060240160e060405180830381865afa15801561058b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105af9190612309565b151560c0880181905290151560a08801526001600160a01b039182166080880152606087019290925291821660408087019190915292821660208087019190915293909116845281518083019092526006825265191b1819981960d11b92820192909252906001146106345760405162461bcd60e51b815260040161035791906122a2565b5060a081015160408051808201909152600681526532363033303360d01b602082015290151560011461067a5760405162461bcd60e51b815260040161035791906122a2565b5060808101516040805180820190915260068152650c8d8c0ccc0d60d21b6020820152906001600160a01b031633146106c65760405162461bcd60e51b815260040161035791906122a2565b5080600001516001600160a01b031663200d2ed26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610709573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061072d91906122ca565b60408051808201909152600681526532363033303560d01b602082015290151560011461076d5760405162461bcd60e51b815260040161035791906122a2565b5080600001516001600160a01b0316636f3b993f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d491906122ca565b604080518082019091526006815265191b1819981b60d11b602082015290156108105760405162461bcd60e51b815260040161035791906122a2565b506001546040820151825160608401516001600160a01b039093169263b5c9cbbe929190610848906108428484611acb565b90611f60565b6040518463ffffffff1660e01b8152600401610866939291906122e5565b6020604051808303816000875af1158015610885573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a991906122ca565b506001546020820151825160608401516001600160a01b039093169263d04c23af9291906108e1906108db8484610a9a565b90611f6c565b6040518463ffffffff1660e01b81526004016108ff939291906122e5565b6020604051808303816000875af115801561091e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094291906122ca565b50600154815160208301516040808501516060860151608087015160a0880151935163ecf8959760e01b81526001600160a01b039097169663ecf895979661099c968c969195909490939092909190600090600401612393565b600060405180830381600087803b1580156109b657600080fd5b505af11580156109ca573d6000803e3d6000fd5b5050505080600001516001600160a01b0316837fae13fd5bb7db89e0f8059ff395d808a71fc16b9ea25a1e8be803f1419575902e8360200151846040015185606001518660800151604051610a2294939291906123dc565b60405180910390a380604001516001600160a01b031681602001516001600160a01b031682600001516001600160a01b03167f2458504403156ef04185b3cb0aab362f0421374a9fccb513fcf87c416324d40e8460600151604051610a8991815260200190565b60405180910390a450600192915050565b6001546040516304bec80960e11b81526001600160a01b0384811660048301528381166024830152600092169063097d9012906044015b602060405180830381865afa158015610aee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b129190612243565b90505b92915050565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa158015610b65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b899190612243565b8211156040518060400160405280600681526020016532363031303160d01b81525090610bc95760405162461bcd60e51b815260040161035791906122a2565b50610bd2611f78565b60015460405163de8211a960e01b8152600481018590526001600160a01b039091169063de8211a99060240160e060405180830381865afa158015610c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3f9190612309565b151560c0880181905290151560a08801526001600160a01b039182166080880152606087019290925291821660408087019190915292821660208087019190915293909116845281518083019092526006825265191b1818981960d11b9282019290925290600114610cc45760405162461bcd60e51b815260040161035791906122a2565b5060a081015160408051808201909152600681526532363031303360d01b60208201529015610d065760405162461bcd60e51b815260040161035791906122a2565b5080602001516001600160a01b0316336001600160a01b03161480610d40575080604001516001600160a01b0316336001600160a01b0316145b604051806040016040528060068152602001650c8d8c0c4c0d60d21b81525090610d7d5760405162461bcd60e51b815260040161035791906122a2565b506001546020820151825160608401516001600160a01b039093169263b5c9cbbe929190610daf906108428484611acb565b6040518463ffffffff1660e01b8152600401610dcd939291906122e5565b6020604051808303816000875af1158015610dec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1091906122ca565b506001546020820151825160608401516001600160a01b039093169263d04c23af929190610e42906108db8484610a9a565b6040518463ffffffff1660e01b8152600401610e60939291906122e5565b6020604051808303816000875af1158015610e7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea391906122ca565b50600154815160208301516040808501516060860151608087015160a0880151935163ecf8959760e01b81526001600160a01b039097169663ecf8959796610efd968c969195909490939092909190600090600401612393565b600060405180830381600087803b158015610f1757600080fd5b505af1158015610f2b573d6000803e3d6000fd5b5050505080600001516001600160a01b0316837f83f6050bab67e325a246d7c944af50a51620253dcae7243de09efa23b2fb52238360200151846040015185606001518660800151604051610f8394939291906123dc565b60405180910390a350600192915050565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa158015610fde573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110029190612243565b8211156040518060400160405280600681526020016532363034303160d01b815250906110425760405162461bcd60e51b815260040161035791906122a2565b5061104b611f78565b60015460405163de8211a960e01b8152600481018590526001600160a01b039091169063de8211a99060240160e060405180830381865afa158015611094573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b89190612309565b151560c0880181905290151560a08801526001600160a01b039182166080880152606087019290925291821660408087019190915292821660208087019190915293909116845281518083019092526006825265191b181a181960d11b928201929092529060011461113d5760405162461bcd60e51b815260040161035791906122a2565b5060a081015160408051808201909152600681526532363034303360d01b60208201529015156001146111835760405162461bcd60e51b815260040161035791906122a2565b5080608001516001600160a01b0316336001600160a01b031614604051806040016040528060068152602001650c8d8c0d0c0d60d21b815250906111da5760405162461bcd60e51b815260040161035791906122a2565b506001546020820151825160608401516001600160a01b039093169263b5c9cbbe92919061120c906108428484611acb565b6040518463ffffffff1660e01b815260040161122a939291906122e5565b6020604051808303816000875af1158015611249573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126d91906122ca565b506001546020820151825160608401516001600160a01b039093169263d04c23af92919061129f906108db8484610a9a565b6040518463ffffffff1660e01b81526004016112bd939291906122e5565b6020604051808303816000875af11580156112dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130091906122ca565b50600154815160208301516040808501516060860151608087015160a0880151935163ecf8959760e01b81526001600160a01b039097169663ecf895979661135a968c969195909490939092909190600090600401612393565b600060405180830381600087803b15801561137457600080fd5b505af1158015611388573d6000803e3d6000fd5b5050505080600001516001600160a01b0316837f37c971bd668c8d1b347a40a1db0d7338d910adcf4d93dc7f8a95b468ddda58828360200151846040015185606001518660800151604051610f8394939291906123dc565b60008084116040518060400160405280600681526020016532363030303160d01b815250906114225760405162461bcd60e51b815260040161035791906122a2565b508361142e3388611acb565b101560405180604001604052806006815260200165191b1818181960d11b8152509061146d5760405162461bcd60e51b815260040161035791906122a2565b50856001600160a01b031663200d2ed26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d091906122ca565b60408051808201909152600681526532363030303360d01b60208201529015156001146115105760405162461bcd60e51b815260040161035791906122a2565b50856001600160a01b0316636f3b993f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561154f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157391906122ca565b6040805180820190915260068152650c8d8c0c0c0d60d21b602082015290156115af5760405162461bcd60e51b815260040161035791906122a2565b50600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa1580156115fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161e9190612243565b61162990600161241d565b600154604051637ea25c9760e11b8152600481018390529192506001600160a01b03169063fd44b92e90602401600060405180830381600087803b15801561167057600080fd5b505af1158015611684573d6000803e3d6000fd5b50506001805460405163ecf8959760e01b81526001600160a01b03909116935063ecf8959792506116c69185918c9133918d918d918d91600091600401612393565b600060405180830381600087803b1580156116e057600080fd5b505af11580156116f4573d6000803e3d6000fd5b50506001546001600160a01b0316915063b5c9cbbe9050338961171b896108db8484611acb565b6040518463ffffffff1660e01b8152600401611739939291906122e5565b6020604051808303816000875af1158015611758573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177c91906122ca565b506001546001600160a01b031663d04c23af338961179e896108428484610a9a565b6040518463ffffffff1660e01b81526004016117bc939291906122e5565b6020604051808303816000875af11580156117db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ff91906122ca565b50866001600160a01b0316817f4a2cf5c0983fc0948d3f0751b04bdf091d03d7a1a5d757d1c93b5a8f2f295f413389898989604051611842959493929190612430565b60405180910390a39695505050505050565b6001546001600160a01b031663b5c9cbbe8433611875866108428484611acb565b6040518463ffffffff1660e01b8152600401611893939291906122e5565b6020604051808303816000875af11580156118b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d691906122ca565b506040516001600160a01b0384169033907f76dacbd9b11b7bbe3372e54b4adc1eeb5724ff92f91142a1a45722c28582ebfd90600090a3505050565b6000805b828110156119495761193f84848381811061193357611933612476565b9050602002013561048b565b9150600101611916565b5092915050565b60015460405163de8211a960e01b8152600481018390526000918291829182918291829182916001600160a01b03169063de8211a99060240160e060405180830381865afa1580156119a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ca9190612309565b959e949d50929b5090995097509550909350915050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611a2a5760405162461bcd60e51b815260040161035791906122a2565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b038216611a6f5760405162461bcd60e51b815260040161035791906122a2565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60015460405163d4fac45d60e01b81526001600160a01b0384811660048301528381166024830152600092169063d4fac45d90604401610ad1565b600154604080516399933b3360e01b815290516000926001600160a01b0316916399933b339160048083019260209291908290030181865afa158015611b50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b749190612243565b8211156040518060400160405280600681526020016532363032303160d01b81525090611bb45760405162461bcd60e51b815260040161035791906122a2565b50611bbd611f78565b60015460405163de8211a960e01b8152600481018590526001600160a01b039091169063de8211a99060240160e060405180830381865afa158015611c06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c2a9190612309565b151560c0880181905290151560a08801526001600160a01b039182166080880152606087019290925291821660408087019190915292821660208087019190915293909116845281518083019092526006825265191b1819181960d11b9282019290925290600114611caf5760405162461bcd60e51b815260040161035791906122a2565b5060a081015160408051808201909152600681526532363032303360d01b60208201529015611cf15760405162461bcd60e51b815260040161035791906122a2565b50604081810151815180830190925260068252650c8d8c0c8c0d60d21b60208301526001600160a01b03163314611d3b5760405162461bcd60e51b815260040161035791906122a2565b5080600001516001600160a01b031663200d2ed26040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da291906122ca565b60408051808201909152600681526532363032303560d01b6020820152901515600114611de25760405162461bcd60e51b815260040161035791906122a2565b5080600001516001600160a01b0316636f3b993f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e4991906122ca565b604080518082019091526006815265191b1819181b60d11b60208201529015611e855760405162461bcd60e51b815260040161035791906122a2565b5060018054825160208401516040808601516060870151608088015160c0890151935163ecf8959760e01b81526001600160a01b039097169763ecf8959797611eda978d979096909594939291600401612393565b600060405180830381600087803b158015611ef457600080fd5b505af1158015611f08573d6000803e3d6000fd5b5050505080600001516001600160a01b0316837ffa1e4d4f57c605169fce32ba32e2fd705e590b8337ad5a79c677440230e4482d8360200151846040015185606001518660800151604051610f8394939291906123dc565b6000610b12828461241d565b6000610b12828461248c565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6001600160a01b0381168114611fc957600080fd5b50565b600060208284031215611fde57600080fd5b8135611fe981611fb4565b9392505050565b60006020828403121561200257600080fd5b5035919050565b6000806040838503121561201c57600080fd5b823561202781611fb4565b9150602083013561203781611fb4565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff8084111561207357612073612042565b604051601f8501601f19908116603f0116810190828211818310171561209b5761209b612042565b816040528093508581528686860111156120b457600080fd5b858560208301376000602087830101525050509392505050565b600080600080600060a086880312156120e657600080fd5b85356120f181611fb4565b9450602086013561210181611fb4565b935060408601359250606086013561211881611fb4565b9150608086013567ffffffffffffffff81111561213457600080fd5b8601601f8101881361214557600080fd5b61215488823560208401612058565b9150509295509295909350565b60008060006060848603121561217657600080fd5b833561218181611fb4565b925060208401359150604084013567ffffffffffffffff8111156121a457600080fd5b8401601f810186136121b557600080fd5b6121c486823560208401612058565b9150509250925092565b600080602083850312156121e157600080fd5b823567ffffffffffffffff808211156121f957600080fd5b818501915085601f83011261220d57600080fd5b81358181111561221c57600080fd5b8660208260051b850101111561223157600080fd5b60209290920196919550909350505050565b60006020828403121561225557600080fd5b5051919050565b6000815180845260005b8181101561228257602081850181015186830182015201612266565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610b12602083018461225c565b805180151581146122c557600080fd5b919050565b6000602082840312156122dc57600080fd5b610b12826122b5565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600080600080600080600060e0888a03121561232457600080fd5b875161232f81611fb4565b602089015190975061234081611fb4565b604089015190965061235181611fb4565b606089015160808a0151919650945061236981611fb4565b925061237760a089016122b5565b915061238560c089016122b5565b905092959891949750929550565b9788526001600160a01b03968716602089015294861660408801529285166060870152608086019190915290921660a084015290151560c0830152151560e08201526101000190565b6001600160a01b03948516815292841660208401526040830191909152909116606082015260800190565b634e487b7160e01b600052601160045260246000fd5b80820180821115610b1557610b15612407565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a06080820181905260009061246b9083018461225c565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b81810381811115610b1557610b1561240756fea264697066735822122057d989c84f5a6d77f74e97300e91e6e78cca2d3270dca8553354c5bf221ba45064736f6c63430008170033" +} \ No newline at end of file diff --git a/contracts/IbetSecurityTokenInterface.json b/contracts/IbetSecurityTokenInterface.json index a9618945..7f749462 100644 --- a/contracts/IbetSecurityTokenInterface.json +++ b/contracts/IbetSecurityTokenInterface.json @@ -804,6 +804,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "requirePersonalInfoRegistered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -843,6 +856,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_requireRegistered", + "type": "bool" + } + ], + "name": "setRequirePersonalInfoRegistered", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { diff --git a/contracts/IbetShare.json b/contracts/IbetShare.json index 444d22e6..4bfb9897 100644 --- a/contracts/IbetShare.json +++ b/contracts/IbetShare.json @@ -1045,6 +1045,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "requirePersonalInfoRegistered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1146,6 +1159,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_requireRegistered", + "type": "bool" + } + ], + "name": "setRequirePersonalInfoRegistered", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1371,6 +1397,6 @@ "type": "function" } ], - "bytecode": "60806040523480156200001157600080fd5b5060405162003c0b38038062003c0b8339810160408190526200003491620001da565b60008054600160a060020a031916331790556001620000548a826200038a565b5060026200006389826200038a565b5060008054600160a060020a03191633179055600f87905560128190556003869055601485905560156200009885826200038a565b506016620000a784826200038a565b506010620000b683826200038a565b50506013805460ff19908116909155600780549091166001179055505060035460008054600160a060020a0316815260086020526040902055506200045c945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126200013d57600080fd5b81516001604060020a03808211156200015a576200015a620000fc565b604051601f8301601f19908116603f01168101908282118183101715620001855762000185620000fc565b81604052838152602092508683858801011115620001a257600080fd5b600091505b83821015620001c65785820183015181830184015290820190620001a7565b600093810190920192909252949350505050565b60008060008060008060008060006101208a8c031215620001fa57600080fd5b89516001604060020a03808211156200021257600080fd5b620002208d838e016200012b565b9a5060208c01519150808211156200023757600080fd5b620002458d838e016200012b565b995060408c0151985060608c0151975060808c0151965060a08c01519150808211156200027157600080fd5b6200027f8d838e016200012b565b955060c08c01519150808211156200029657600080fd5b620002a48d838e016200012b565b945060e08c0151915080821115620002bb57600080fd5b50620002ca8c828d016200012b565b9250506101008a015190509295985092959850929598565b600281046001821680620002f757607f821691505b60208210810362000331577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111562000385576000818152602081206020601f86010481016020861015620003605750805b6020601f860104820191505b8181101562000381578281556001016200036c565b5050505b505050565b81516001604060020a03811115620003a657620003a6620000fc565b620003be81620003b78454620002e2565b8462000337565b602080601f831160018114620003fa5760008415620003dd5750858301515b60028086026008870290910a600019041982161786555062000381565b600085815260208120601f198616915b828110156200042b578886015182559484019460019091019084016200040a565b50858210156200044c57878501516008601f88160260020a60001904191681555b5050505050600202600101905550565b61379f806200046c6000396000f3fe608060405234801561001057600080fd5b506004361061035f576000357c01000000000000000000000000000000000000000000000000000000009004806381ebdd79116101d5578063b9b3e06a11610121578063db20266f116100bf578063f2fde38b11610099578063f2fde38b1461073e578063f7502a7c14610751578063fef1b9c014610764578063ff12dcea146107a457600080fd5b8063db20266f146106f3578063e7464db11461071e578063edaab2081461073157600080fd5b8063c7109ec9116100fb578063c7109ec9146106a7578063ca5cbbb5146106ba578063cb761015146106cd578063d9322058146106e057600080fd5b8063b9b3e06a1461066e578063bfe30fb014610681578063c267ce5f1461069457600080fd5b80639301a78b1161018e5780639cd23707116101685780639cd237071461060f578063a9059cbb14610622578063b2a90a6014610635578063b967a52e1461065b57600080fd5b80639301a78b146105d257806395d89b41146105f4578063996f3a4b146105fc57600080fd5b806381ebdd791461056857806383850bcb146105885780638da5cb5b146105905780638db9877c146105a35780638ed8a9bc146105ab57806392ff0d31146105be57600080fd5b806331cef447116102af5780635c2c20051161024d5780636f3b993f116102275780636f3b993f1461052c57806370a082311461053957806376fa7a311461054c5780637da68d341461055f57600080fd5b80635c2c2005146104fd5780635c40f6f4146105065780635ccef3e71461051957600080fd5b806340eba90e1161028957806340eba90e146104c3578063428630f6146104d657806358c3b870146104de5780635b473327146104e657600080fd5b806331cef4471461049557806336f7ab5e146104a857806340615cf8146104b057600080fd5b80631935a8801161031c57806325287d42116102f657806325287d421461043c57806325d608611461044f57806327e235e3146104625780632e027abe1461048257600080fd5b80631935a88014610409578063200d2ed21461041c57806323b872dd1461042957600080fd5b806306eaa0b71461036457806306fdde03146103795780630af7eb0f146103975780630ed5a933146103c2578063153a1f3e146103df57806318160ddd146103f2575b600080fd5b610377610372366004612ed5565b6107b7565b005b610381610ab5565b60405161038e9190612f62565b60405180910390f35b6009546103aa90600160a060020a031681565b604051600160a060020a03909116815260200161038e565b6013546103cf9060ff1681565b604051901515815260200161038e565b6103cf6103ed36600461301d565b610b43565b6103fb60035481565b60405190815260200161038e565b6103776104173660046130e1565b610e06565b6007546103cf9060ff1681565b6103cf6104373660046130fe565b610e9d565b6004546103aa90600160a060020a031681565b61037761045d36600461313a565b6110bd565b6103fb610470366004613177565b60086020526000908152604090205481565b610381610490366004613192565b61111d565b6103776104a3366004612ed5565b611136565b6103816112f9565b6103776104be3660046131b5565b611306565b6103776104d13660046131df565b6113aa565b6103816116cf565b6103816116dc565b6104ee6116e9565b60405161038e93929190613236565b6103fb600f5481565b6103776105143660046130e1565b61180e565b610377610527366004612ed5565b6118a3565b600b546103cf9060ff1681565b6103fb610547366004613177565b611c5f565b61037761055a366004613177565b611c7a565b6103fb60125481565b6103fb610576366004613177565b600d6020526000908152604090205481565b610377611cf9565b6000546103aa90600160a060020a031681565b610381611d81565b6103776105b936600461313a565b611d8e565b6009546103cf9060a060020a900460ff1681565b6105e56105e0366004613177565b611dea565b60405161038e9392919061326b565b610381611e12565b61037761060a366004613293565b611e1f565b61037761061d3660046130e1565b611ed2565b6103cf6106303660046131b5565b611f51565b6009546103cf907501000000000000000000000000000000000000000000900460ff1681565b61037761066936600461313a565b612046565b6103fb61067c3660046132f6565b6120a2565b61037761068f366004613329565b6120cd565b6103776106a23660046131df565b6121d0565b6103776106b53660046130e1565b6122c7565b6103776106c8366004613391565b612384565b6103776106db36600461340a565b6124f3565b6103776106ee36600461313a565b612548565b6103fb6107013660046132f6565b600e60209081526000928352604080842090915290825290205481565b61037761072c3660046130fe565b6125a4565b6017546103cf9060ff1681565b61037761074c366004613177565b6127ac565b61037761075f366004613177565b6128c3565b61077761077236600461340a565b612942565b60408051600160a060020a039586168152949093166020850152918301521515606082015260800161038e565b6103776107b23660046130fe565b61298b565b33600160a060020a0316600c83815481106107d4576107d4613423565b6000918252602090912060049091020154600160a060020a0316148015906108075750600054600160a060020a03163314155b1561086357604080518082018252600681527f31313038303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b60405180910390fd5b600c828154811061087657610876613423565b6000918252602082206003600490920201015460ff16151590036108e257604080518082018252600681527f31313038303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600c82815481106108f5576108f5613423565b90600052602060002090600402016002015460086000600c858154811061091e5761091e613423565b60009182526020808320600490920290910154600160a060020a0316835282019290925260400181208054909190610957908490613455565b9091555050600c80548390811061097057610970613423565b906000526020600020906004020160020154600d6000600c858154811061099957610999613423565b60009182526020808320600490920290910154600160a060020a03168352820192909252604001812080549091906109d2908490613468565b925050819055506000600c83815481106109ee576109ee613423565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817fa0cc3a4feb26502e6e6cd57b2113d58bdab80c6711d8252356be9a49945bb5f7600c8481548110610a4b57610a4b613423565b6000918252602090912060049091020154600c8054600160a060020a039092169186908110610a7c57610a7c613423565b6000918252602090912060016004909202010154604051610aa99291600160a060020a031690869061347b565b60405180910390a25050565b60018054610ac2906134a7565b80601f0160208091040260200160405190810160405280929190818152602001828054610aee906134a7565b8015610b3b5780601f10610b1057610100808354040283529160200191610b3b565b820191906000526020600020905b815481529060010190602001808311610b1e57829003601f168201915b505050505081565b600b5460009060ff161515600103610ba357604080518082018252600681527f31313035303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b8151835114610bfa57604080518082018252600681527f31313035303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b6000805b8451811015610c4057838181518110610c1957610c19613423565b602002602001015182610c2c9190613455565b915080610c38816134e4565b915050610bfe565b5080610c4b33611c5f565b1015610c9f57604080518082018252600681527f31313035303300000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600454600160a060020a03163314610d1a5760095460408051808201909152600681527f313130353034000000000000000000000000000000000000000000000000000060208201529060a060020a900460ff161515600114610d185760405160e560020a62461bcd02815260040161085a9190612f62565b505b606060006001935060005b8651811015610dfb57610d51878281518110610d4357610d43613423565b60200260200101513b151590565b15610d9a57610d93878281518110610d6b57610d6b613423565b6020026020010151878381518110610d8557610d85613423565b602002602001015185612ab9565b9150610dda565b610dd7878281518110610daf57610daf613423565b6020026020010151878381518110610dc957610dc9613423565b602002602001015185612c07565b91505b811515600003610de957600094505b80610df3816134e4565b915050610d25565b505050505b92915050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314610e555760405160e560020a62461bcd02815260040161085a9190612f62565b50600b805460ff19168215159081179091556040519081527fe1455bc53682229469d6919bb96d7b338cbffea93170dfb5f02242c22fb6d07f9060200160405180910390a150565b60008054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314610eed5760405160e560020a62461bcd02815260040161085a9190612f62565b5081610ef885611c5f565b1015610f4c57604080518082018252600681527f31313036303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b6060833b1561102a57610f6883610f6287611c5f565b90612dfc565b600160a060020a038616600090815260086020526040902055610f9483610f8e86611c5f565b90612e0f565b600160a060020a038516600081815260086020526040908190209290925590517fc0ee0b8a00000000000000000000000000000000000000000000000000000000815285919063c0ee0b8a90610ff2903390889087906004016134fd565b600060405180830381600087803b15801561100c57600080fd5b505af1158015611020573d6000803e3d6000fd5b5050505050611077565b61103783610f6287611c5f565b600160a060020a03861660009081526008602052604090205561105d83610f8e86611c5f565b600160a060020a0385166000908152600860205260409020555b83600160a060020a031685600160a060020a031660008051602061374a833981519152856040516110aa91815260200190565b60405180910390a3506001949350505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a0316331461110c5760405160e560020a62461bcd02815260040161085a9190612f62565b5060116111198282613574565b5050565b60186020526000908152604090208054610ac2906134a7565b60095460408051808201909152600681527f31313130303100000000000000000000000000000000000000000000000000006020820152907501000000000000000000000000000000000000000000900460ff1615156001146111af5760405160e560020a62461bcd02815260040161085a9190612f62565b506009546000546040517fd3da927f000000000000000000000000000000000000000000000000000000008152336004820152600160a060020a03918216602482015291169063d3da927f90604401602060405180830381865afa15801561121b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123f919061363a565b60408051808201909152600681527f313131303032000000000000000000000000000000000000000000000000000060208201529015156001146112995760405160e560020a62461bcd02815260040161085a9190612f62565b50336000908152600a602052604090208281556002016112b98282613574565b507f3bb9553f633a7ecb4cbc5f9885e750a522496ce06deff1ed9184180ca83940c03383836040516112ed939291906134fd565b60405180910390a15050565b60058054610ac2906134a7565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146113555760405160e560020a62461bcd02815260040161085a9190612f62565b50600160a060020a0382166000818152600a6020908152604091829020600101849055815192835282018390527fa42bd6bdfcb8f9b815c68e4ce0e16dec3f1e7ac53967ee8309653de522a735b191016112ed565b600b5460ff1615806113c6575060095460a060020a900460ff16155b806113d85750816113d633611c5f565b105b1561142b57604080518082018252600681527f31313037303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600054600160a060020a0384811691161461152d576009546000546040517fd3da927f000000000000000000000000000000000000000000000000000000008152600160a060020a038681166004830152918216602482015291169063d3da927f90604401602060405180830381865afa1580156114ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d1919061363a565b60408051808201909152600681527f3131303730320000000000000000000000000000000000000000000000000000602082015290151560011461152b5760405160e560020a62461bcd02815260040161085a9190612f62565b505b336000908152600860205260408120805484929061154c908490613468565b9091555050336000908152600d602052604081208054849290611570908490613455565b9091555050600c80546040805160808101825233808252600160a060020a038881166020840190815283850189815260016060860181815290880189556000989098529351600487027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c78101805492851673ffffffffffffffffffffffffffffffffffffffff1993841617905591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8830180549190941691161790915591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c983015593517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca909101805491151560ff1990921691909117905551909182917fa256c3bc72c26fbfd6285cfde8c7e345b4d0c7ccfc725d103e9f129c5d8a039a916116c191889088908890613657565b60405180910390a250505050565b60068054610ac2906134a7565b60118054610ac2906134a7565b60148054601580549192916116fd906134a7565b80601f0160208091040260200160405190810160405280929190818152602001828054611729906134a7565b80156117765780601f1061174b57610100808354040283529160200191611776565b820191906000526020600020905b81548152906001019060200180831161175957829003601f168201915b50505050509080600201805461178b906134a7565b80601f01602080910402602001604051908101604052809291908181526020018280546117b7906134a7565b80156118045780601f106117d957610100808354040283529160200191611804565b820191906000526020600020905b8154815290600101906020018083116117e757829003601f168201915b5050505050905083565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a0316331461185d5760405160e560020a62461bcd02815260040161085a9190612f62565b506007805460ff191682151590811790915560405160ff9091161515907fe1e5c6f5867805bf3fab73f4df4b80d0e87e9262a7bc22ac7c2cb3fd5896222f90600090a250565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146118f25760405160e560020a62461bcd02815260040161085a9190612f62565b5060095460a060020a900460ff16151560000361195757604080518082018252600681527f31313039303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600c828154811061196a5761196a613423565b6000918252602082206003600490920201015460ff16151590036119d657604080518082018252600681527f31313039303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600c82815481106119e9576119e9613423565b90600052602060002090600402016002015460086000600c8581548110611a1257611a12613423565b6000918252602080832060016004909302019190910154600160a060020a0316835282019290925260400181208054909190611a4f908490613455565b9091555050600c805483908110611a6857611a68613423565b906000526020600020906004020160020154600d6000600c8581548110611a9157611a91613423565b60009182526020808320600490920290910154600160a060020a0316835282019290925260400181208054909190611aca908490613468565b925050819055506000600c8381548110611ae657611ae6613423565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817f6b50d6e4941ce7ae74ed7f7f51df5fb7680754c2815a094c66bea5ca80a40eb1600c8481548110611b4357611b43613423565b6000918252602090912060049091020154600c8054600160a060020a039092169186908110611b7457611b74613423565b6000918252602090912060016004909202010154604051611ba19291600160a060020a031690869061347b565b60405180910390a2600c8281548110611bbc57611bbc613423565b6000918252602090912060016004909202010154600c8054600160a060020a039092169184908110611bf057611bf0613423565b6000918252602090912060049091020154600c8054600160a060020a039092169160008051602061374a833981519152919086908110611c3257611c32613423565b906000526020600020906004020160020154604051611c5391815260200190565b60405180910390a35050565b600160a060020a031660009081526008602052604090205490565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611cc95760405160e560020a62461bcd02815260040161085a9190612f62565b506004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611d485760405160e560020a62461bcd02815260040161085a9190612f62565b506013805460ff191660011790556040517f8298c3671093b19970d7c94ce1f23925a962f36fec31d25075d9be072b73e10390600090a1565b60108054610ac2906134a7565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611ddd5760405160e560020a62461bcd02815260040161085a9190612f62565b5060066111198282613574565b600a602052600090815260409020805460018201546002830180549293919261178b906134a7565b60028054610ac2906134a7565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611e6e5760405160e560020a62461bcd02815260040161085a9190612f62565b5060148390556015611e808382613574565b506016611e8d8282613574565b506014546040517ff89b6034f2b759ebb2dfb08461be917a1b54f2e0523c895e7f9b5e5e14dd013e91611ec591601590601690613705565b60405180910390a1505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611f215760405160e560020a62461bcd02815260040161085a9190612f62565b506009805491151560a060020a0274ff000000000000000000000000000000000000000019909216919091179055565b600081611f5d33611c5f565b10156040518060400160405280600681526020017f313130343031000000000000000000000000000000000000000000000000000081525090611fb65760405160e560020a62461bcd02815260040161085a9190612f62565b5060095460408051808201909152600681527f313130343032000000000000000000000000000000000000000000000000000060208201529060a060020a900460ff16151560011461201e5760405160e560020a62461bcd02815260040161085a9190612f62565b506060833b1561203b57612033848483612ab9565b915050610e00565b612033848483612c07565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146120955760405160e560020a62461bcd02815260040161085a9190612f62565b5060056111198282613574565b600160a060020a039182166000908152600e6020908152604080832093909416825291909152205490565b816120d833866120a2565b101561212c57604080518082018252600681527f31313031303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61213a82610f6233876120a2565b336000908152600e60209081526040808320600160a060020a038916845290915290205561216b82610f8e85611c5f565b600160a060020a038085166000908152600860205260409081902092909255905133918616907f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc906121c2908790879087906134fd565b60405180910390a350505050565b816121da33611c5f565b101561222e57604080518082018252600681527f31313030303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61223b82610f6233611c5f565b3360008181526008602052604090209190915561225f908390610f8e9086906120a2565b600160a060020a0384166000818152600e602090815260408083203380855292529182902093909355519091907ecd77abc69e7bbb6de9df8cbfcca8720df3a30decf9eefd187e72ab2ea2f513906122ba9086908690613730565b60405180910390a3505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146123165760405160e560020a62461bcd02815260040161085a9190612f62565b506009805475ff00000000000000000000000000000000000000000019167501000000000000000000000000000000000000000000831515908102919091179091556040517fa0c691180b8089c0c84e478ee67d6325768666cd889f13de016771c6f7af1f8890600090a250565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146123d35760405160e560020a62461bcd02815260040161085a9190612f62565b50816123df86866120a2565b101561243357604080518082018252600681527f31313132303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61244182610f6287876120a2565b600160a060020a038087166000908152600e602090815260408083209389168352929052205561247482610f8e85611c5f565b6008600085600160a060020a0316600160a060020a031681526020019081526020016000208190555084600160a060020a031684600160a060020a03167f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc8585856040516124e4939291906134fd565b60405180910390a35050505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146125425760405160e560020a62461bcd02815260040161085a9190612f62565b50601255565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146125975760405160e560020a62461bcd02815260040161085a9190612f62565b5060106111198282613574565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146125f35760405160e560020a62461bcd02815260040161085a9190612f62565b50600160a060020a038216156126ab578061260e83856120a2565b101561266257604080518082018252600681527f31313131303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61267081610f6284866120a2565b600160a060020a038084166000908152600e60209081526040808320938816835292905220556003546126a39082612dfc565b600355612750565b600160a060020a03831660009081526008602052604090205481111561271957604080518082018252600681527f31313131303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61272681610f6285611c5f565b600160a060020a03841660009081526008602052604090205560035461274c9082612dfc565b6003555b81600160a060020a031683600160a060020a031633600160a060020a03167fee02732fab40ece8284c756220846dff4b8d32058b86b35b4f0459bf172fcef08460405161279f91815260200190565b60405180910390a4505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146127fb5760405160e560020a62461bcd02815260040161085a9190612f62565b5060408051808201909152600681527f35303031303100000000000000000000000000000000000000000000000000006020820152600160a060020a03821661285a5760405160e560020a62461bcd02815260040161085a9190612f62565b5060008054604051600160a060020a03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146129125760405160e560020a62461bcd02815260040161085a9190612f62565b506009805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600c818154811061295257600080fd5b60009182526020909120600490910201805460018201546002830154600390930154600160a060020a0392831694509116919060ff1684565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146129da5760405160e560020a62461bcd02815260040161085a9190612f62565b50600160a060020a03821615612a33576129f881610f8e84866120a2565b600160a060020a038084166000908152600e6020908152604080832093881683529290522055600354612a2b9082612e0f565b600355612a6a565b612a4081610f8e85611c5f565b600160a060020a038416600090815260086020526040902055600354612a669082612e0f565b6003555b81600160a060020a031683600160a060020a031633600160a060020a03167f65adeb76912378393e600cb6f64f3310842a42e1eae86273dc775d0c47a0f2dc8460405161279f91815260200190565b60045460408051808201909152600681527f31313033303100000000000000000000000000000000000000000000000000006020820152600091600160a060020a03868116911614612b215760405160e560020a62461bcd02815260040161085a9190612f62565b50612b2f83610f6233611c5f565b33600090815260086020526040902055612b4c83610f8e86611c5f565b600160a060020a038516600081815260086020526040908190209290925590517fc0ee0b8a00000000000000000000000000000000000000000000000000000000815285919063c0ee0b8a90612baa903390889088906004016134fd565b600060405180830381600087803b158015612bc457600080fd5b505af1158015612bd8573d6000803e3d6000fd5b5050604051868152600160a060020a038816925033915060008051602061374a833981519152906020016110aa565b600454600090600160a060020a03163314801590612c2c5750600b5460ff1615156001145b15612c7f57604080518082018252600681527f31313032303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600054600160a060020a03858116911614612d81576009546000546040517fd3da927f000000000000000000000000000000000000000000000000000000008152600160a060020a038781166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015612d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d25919061363a565b60408051808201909152600681527f31313032303200000000000000000000000000000000000000000000000000006020820152901515600114612d7f5760405160e560020a62461bcd02815260040161085a9190612f62565b505b612d8e83610f6233611c5f565b33600090815260086020526040902055612dab83610f8e86611c5f565b600160a060020a03851660008181526008602052604090819020929092559051339060008051602061374a83398151915290612dea9087815260200190565b60405180910390a35060019392505050565b6000612e088284613468565b9392505050565b6000612e088284613455565b60e060020a634e487b7102600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612e5d57612e5d612e1b565b604052919050565b600082601f830112612e7657600080fd5b813567ffffffffffffffff811115612e9057612e90612e1b565b612ea3601f8201601f1916602001612e34565b818152846020838601011115612eb857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215612ee857600080fd5b82359150602083013567ffffffffffffffff811115612f0657600080fd5b612f1285828601612e65565b9150509250929050565b6000815180845260005b81811015612f4257602081850181015186830182015201612f26565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612e086020830184612f1c565b600067ffffffffffffffff821115612f8f57612f8f612e1b565b5060209081020190565b8035600160a060020a0381168114612fb057600080fd5b919050565b600082601f830112612fc657600080fd5b81356020612fdb612fd683612f75565b612e34565b82815291810284018101918181019086841115612ff757600080fd5b8286015b848110156130125780358352918301918301612ffb565b509695505050505050565b6000806040838503121561303057600080fd5b823567ffffffffffffffff8082111561304857600080fd5b818501915085601f83011261305c57600080fd5b8135602061306c612fd683612f75565b8281529181028401810191818101908984111561308857600080fd5b948201945b838610156130ad5761309e86612f99565b8252948201949082019061308d565b965050860135925050808211156130c357600080fd5b50612f1285828601612fb5565b80151581146130de57600080fd5b50565b6000602082840312156130f357600080fd5b8135612e08816130d0565b60008060006060848603121561311357600080fd5b61311c84612f99565b925061312a60208501612f99565b9150604084013590509250925092565b60006020828403121561314c57600080fd5b813567ffffffffffffffff81111561316357600080fd5b61316f84828501612e65565b949350505050565b60006020828403121561318957600080fd5b612e0882612f99565b6000602082840312156131a457600080fd5b813560ff81168114612e0857600080fd5b600080604083850312156131c857600080fd5b6131d183612f99565b946020939093013593505050565b6000806000606084860312156131f457600080fd5b6131fd84612f99565b925060208401359150604084013567ffffffffffffffff81111561322057600080fd5b61322c86828701612e65565b9150509250925092565b83815260606020820152600061324f6060830185612f1c565b82810360408401526132618185612f1c565b9695505050505050565b83815282602082015260606040820152600061328a6060830184612f1c565b95945050505050565b6000806000606084860312156132a857600080fd5b83359250602084013567ffffffffffffffff808211156132c757600080fd5b6132d387838801612e65565b935060408601359150808211156132e957600080fd5b5061322c86828701612e65565b6000806040838503121561330957600080fd5b61331283612f99565b915061332060208401612f99565b90509250929050565b6000806000806080858703121561333f57600080fd5b61334885612f99565b935061335660208601612f99565b925060408501359150606085013567ffffffffffffffff81111561337957600080fd5b61338587828801612e65565b91505092959194509250565b600080600080600060a086880312156133a957600080fd5b6133b286612f99565b94506133c060208701612f99565b93506133ce60408701612f99565b925060608601359150608086013567ffffffffffffffff8111156133f157600080fd5b6133fd88828901612e65565b9150509295509295909350565b60006020828403121561341c57600080fd5b5035919050565b60e060020a634e487b7102600052603260045260246000fd5b60e060020a634e487b7102600052601160045260246000fd5b80820180821115610e0057610e0061343c565b81810381811115610e0057610e0061343c565b6000600160a060020a0380861683528085166020840152506060604083015261328a6060830184612f1c565b6002810460018216806134bb57607f821691505b6020821081036134de5760e060020a634e487b7102600052602260045260246000fd5b50919050565b6000600182016134f6576134f661343c565b5060010190565b600160a060020a038416815282602082015260606040820152600061328a6060830184612f1c565b601f82111561356f576000818152602081206020601f8601048101602086101561354c5750805b6020601f860104820191505b8181101561356b57828155600101613558565b5050505b505050565b815167ffffffffffffffff81111561358e5761358e612e1b565b6135a28161359c84546134a7565b84613525565b602080601f8311600181146135db57600084156135bf5750858301515b60028086026008870290910a600019041982161786555061356b565b600085815260208120601f198616915b8281101561360a578886015182559484019460019091019084016135eb565b508582101561362a57878501516008601f88160260020a60001904191681555b5050505050600202600101905550565b60006020828403121561364c57600080fd5b8151612e08816130d0565b6000600160a060020a038087168352808616602084015250836040830152608060608301526132616080830184612f1c565b60008154613696816134a7565b8085526020600183811680156136b357600181146136cc576136fa565b60ff1985168884015283151583028801830195506136fa565b866000528260002060005b858110156136f25781548a82018601529083019084016136d7565b890184019650505b505050505092915050565b83815260606020820152600061371e6060830185613689565b82810360408401526132618185613689565b82815260406020820152600061316f6040830184612f1c56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220333ee6f8177146f6409ff0926887d996f1ed72eb0e938e2d13cbbe5a5033c1b464736f6c63430008110033", - "deployedBytecode": "608060405234801561001057600080fd5b506004361061035f576000357c01000000000000000000000000000000000000000000000000000000009004806381ebdd79116101d5578063b9b3e06a11610121578063db20266f116100bf578063f2fde38b11610099578063f2fde38b1461073e578063f7502a7c14610751578063fef1b9c014610764578063ff12dcea146107a457600080fd5b8063db20266f146106f3578063e7464db11461071e578063edaab2081461073157600080fd5b8063c7109ec9116100fb578063c7109ec9146106a7578063ca5cbbb5146106ba578063cb761015146106cd578063d9322058146106e057600080fd5b8063b9b3e06a1461066e578063bfe30fb014610681578063c267ce5f1461069457600080fd5b80639301a78b1161018e5780639cd23707116101685780639cd237071461060f578063a9059cbb14610622578063b2a90a6014610635578063b967a52e1461065b57600080fd5b80639301a78b146105d257806395d89b41146105f4578063996f3a4b146105fc57600080fd5b806381ebdd791461056857806383850bcb146105885780638da5cb5b146105905780638db9877c146105a35780638ed8a9bc146105ab57806392ff0d31146105be57600080fd5b806331cef447116102af5780635c2c20051161024d5780636f3b993f116102275780636f3b993f1461052c57806370a082311461053957806376fa7a311461054c5780637da68d341461055f57600080fd5b80635c2c2005146104fd5780635c40f6f4146105065780635ccef3e71461051957600080fd5b806340eba90e1161028957806340eba90e146104c3578063428630f6146104d657806358c3b870146104de5780635b473327146104e657600080fd5b806331cef4471461049557806336f7ab5e146104a857806340615cf8146104b057600080fd5b80631935a8801161031c57806325287d42116102f657806325287d421461043c57806325d608611461044f57806327e235e3146104625780632e027abe1461048257600080fd5b80631935a88014610409578063200d2ed21461041c57806323b872dd1461042957600080fd5b806306eaa0b71461036457806306fdde03146103795780630af7eb0f146103975780630ed5a933146103c2578063153a1f3e146103df57806318160ddd146103f2575b600080fd5b610377610372366004612ed5565b6107b7565b005b610381610ab5565b60405161038e9190612f62565b60405180910390f35b6009546103aa90600160a060020a031681565b604051600160a060020a03909116815260200161038e565b6013546103cf9060ff1681565b604051901515815260200161038e565b6103cf6103ed36600461301d565b610b43565b6103fb60035481565b60405190815260200161038e565b6103776104173660046130e1565b610e06565b6007546103cf9060ff1681565b6103cf6104373660046130fe565b610e9d565b6004546103aa90600160a060020a031681565b61037761045d36600461313a565b6110bd565b6103fb610470366004613177565b60086020526000908152604090205481565b610381610490366004613192565b61111d565b6103776104a3366004612ed5565b611136565b6103816112f9565b6103776104be3660046131b5565b611306565b6103776104d13660046131df565b6113aa565b6103816116cf565b6103816116dc565b6104ee6116e9565b60405161038e93929190613236565b6103fb600f5481565b6103776105143660046130e1565b61180e565b610377610527366004612ed5565b6118a3565b600b546103cf9060ff1681565b6103fb610547366004613177565b611c5f565b61037761055a366004613177565b611c7a565b6103fb60125481565b6103fb610576366004613177565b600d6020526000908152604090205481565b610377611cf9565b6000546103aa90600160a060020a031681565b610381611d81565b6103776105b936600461313a565b611d8e565b6009546103cf9060a060020a900460ff1681565b6105e56105e0366004613177565b611dea565b60405161038e9392919061326b565b610381611e12565b61037761060a366004613293565b611e1f565b61037761061d3660046130e1565b611ed2565b6103cf6106303660046131b5565b611f51565b6009546103cf907501000000000000000000000000000000000000000000900460ff1681565b61037761066936600461313a565b612046565b6103fb61067c3660046132f6565b6120a2565b61037761068f366004613329565b6120cd565b6103776106a23660046131df565b6121d0565b6103776106b53660046130e1565b6122c7565b6103776106c8366004613391565b612384565b6103776106db36600461340a565b6124f3565b6103776106ee36600461313a565b612548565b6103fb6107013660046132f6565b600e60209081526000928352604080842090915290825290205481565b61037761072c3660046130fe565b6125a4565b6017546103cf9060ff1681565b61037761074c366004613177565b6127ac565b61037761075f366004613177565b6128c3565b61077761077236600461340a565b612942565b60408051600160a060020a039586168152949093166020850152918301521515606082015260800161038e565b6103776107b23660046130fe565b61298b565b33600160a060020a0316600c83815481106107d4576107d4613423565b6000918252602090912060049091020154600160a060020a0316148015906108075750600054600160a060020a03163314155b1561086357604080518082018252600681527f31313038303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b60405180910390fd5b600c828154811061087657610876613423565b6000918252602082206003600490920201015460ff16151590036108e257604080518082018252600681527f31313038303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600c82815481106108f5576108f5613423565b90600052602060002090600402016002015460086000600c858154811061091e5761091e613423565b60009182526020808320600490920290910154600160a060020a0316835282019290925260400181208054909190610957908490613455565b9091555050600c80548390811061097057610970613423565b906000526020600020906004020160020154600d6000600c858154811061099957610999613423565b60009182526020808320600490920290910154600160a060020a03168352820192909252604001812080549091906109d2908490613468565b925050819055506000600c83815481106109ee576109ee613423565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817fa0cc3a4feb26502e6e6cd57b2113d58bdab80c6711d8252356be9a49945bb5f7600c8481548110610a4b57610a4b613423565b6000918252602090912060049091020154600c8054600160a060020a039092169186908110610a7c57610a7c613423565b6000918252602090912060016004909202010154604051610aa99291600160a060020a031690869061347b565b60405180910390a25050565b60018054610ac2906134a7565b80601f0160208091040260200160405190810160405280929190818152602001828054610aee906134a7565b8015610b3b5780601f10610b1057610100808354040283529160200191610b3b565b820191906000526020600020905b815481529060010190602001808311610b1e57829003601f168201915b505050505081565b600b5460009060ff161515600103610ba357604080518082018252600681527f31313035303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b8151835114610bfa57604080518082018252600681527f31313035303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b6000805b8451811015610c4057838181518110610c1957610c19613423565b602002602001015182610c2c9190613455565b915080610c38816134e4565b915050610bfe565b5080610c4b33611c5f565b1015610c9f57604080518082018252600681527f31313035303300000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600454600160a060020a03163314610d1a5760095460408051808201909152600681527f313130353034000000000000000000000000000000000000000000000000000060208201529060a060020a900460ff161515600114610d185760405160e560020a62461bcd02815260040161085a9190612f62565b505b606060006001935060005b8651811015610dfb57610d51878281518110610d4357610d43613423565b60200260200101513b151590565b15610d9a57610d93878281518110610d6b57610d6b613423565b6020026020010151878381518110610d8557610d85613423565b602002602001015185612ab9565b9150610dda565b610dd7878281518110610daf57610daf613423565b6020026020010151878381518110610dc957610dc9613423565b602002602001015185612c07565b91505b811515600003610de957600094505b80610df3816134e4565b915050610d25565b505050505b92915050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314610e555760405160e560020a62461bcd02815260040161085a9190612f62565b50600b805460ff19168215159081179091556040519081527fe1455bc53682229469d6919bb96d7b338cbffea93170dfb5f02242c22fb6d07f9060200160405180910390a150565b60008054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314610eed5760405160e560020a62461bcd02815260040161085a9190612f62565b5081610ef885611c5f565b1015610f4c57604080518082018252600681527f31313036303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b6060833b1561102a57610f6883610f6287611c5f565b90612dfc565b600160a060020a038616600090815260086020526040902055610f9483610f8e86611c5f565b90612e0f565b600160a060020a038516600081815260086020526040908190209290925590517fc0ee0b8a00000000000000000000000000000000000000000000000000000000815285919063c0ee0b8a90610ff2903390889087906004016134fd565b600060405180830381600087803b15801561100c57600080fd5b505af1158015611020573d6000803e3d6000fd5b5050505050611077565b61103783610f6287611c5f565b600160a060020a03861660009081526008602052604090205561105d83610f8e86611c5f565b600160a060020a0385166000908152600860205260409020555b83600160a060020a031685600160a060020a031660008051602061374a833981519152856040516110aa91815260200190565b60405180910390a3506001949350505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a0316331461110c5760405160e560020a62461bcd02815260040161085a9190612f62565b5060116111198282613574565b5050565b60186020526000908152604090208054610ac2906134a7565b60095460408051808201909152600681527f31313130303100000000000000000000000000000000000000000000000000006020820152907501000000000000000000000000000000000000000000900460ff1615156001146111af5760405160e560020a62461bcd02815260040161085a9190612f62565b506009546000546040517fd3da927f000000000000000000000000000000000000000000000000000000008152336004820152600160a060020a03918216602482015291169063d3da927f90604401602060405180830381865afa15801561121b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123f919061363a565b60408051808201909152600681527f313131303032000000000000000000000000000000000000000000000000000060208201529015156001146112995760405160e560020a62461bcd02815260040161085a9190612f62565b50336000908152600a602052604090208281556002016112b98282613574565b507f3bb9553f633a7ecb4cbc5f9885e750a522496ce06deff1ed9184180ca83940c03383836040516112ed939291906134fd565b60405180910390a15050565b60058054610ac2906134a7565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146113555760405160e560020a62461bcd02815260040161085a9190612f62565b50600160a060020a0382166000818152600a6020908152604091829020600101849055815192835282018390527fa42bd6bdfcb8f9b815c68e4ce0e16dec3f1e7ac53967ee8309653de522a735b191016112ed565b600b5460ff1615806113c6575060095460a060020a900460ff16155b806113d85750816113d633611c5f565b105b1561142b57604080518082018252600681527f31313037303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600054600160a060020a0384811691161461152d576009546000546040517fd3da927f000000000000000000000000000000000000000000000000000000008152600160a060020a038681166004830152918216602482015291169063d3da927f90604401602060405180830381865afa1580156114ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d1919061363a565b60408051808201909152600681527f3131303730320000000000000000000000000000000000000000000000000000602082015290151560011461152b5760405160e560020a62461bcd02815260040161085a9190612f62565b505b336000908152600860205260408120805484929061154c908490613468565b9091555050336000908152600d602052604081208054849290611570908490613455565b9091555050600c80546040805160808101825233808252600160a060020a038881166020840190815283850189815260016060860181815290880189556000989098529351600487027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c78101805492851673ffffffffffffffffffffffffffffffffffffffff1993841617905591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8830180549190941691161790915591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c983015593517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca909101805491151560ff1990921691909117905551909182917fa256c3bc72c26fbfd6285cfde8c7e345b4d0c7ccfc725d103e9f129c5d8a039a916116c191889088908890613657565b60405180910390a250505050565b60068054610ac2906134a7565b60118054610ac2906134a7565b60148054601580549192916116fd906134a7565b80601f0160208091040260200160405190810160405280929190818152602001828054611729906134a7565b80156117765780601f1061174b57610100808354040283529160200191611776565b820191906000526020600020905b81548152906001019060200180831161175957829003601f168201915b50505050509080600201805461178b906134a7565b80601f01602080910402602001604051908101604052809291908181526020018280546117b7906134a7565b80156118045780601f106117d957610100808354040283529160200191611804565b820191906000526020600020905b8154815290600101906020018083116117e757829003601f168201915b5050505050905083565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a0316331461185d5760405160e560020a62461bcd02815260040161085a9190612f62565b506007805460ff191682151590811790915560405160ff9091161515907fe1e5c6f5867805bf3fab73f4df4b80d0e87e9262a7bc22ac7c2cb3fd5896222f90600090a250565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146118f25760405160e560020a62461bcd02815260040161085a9190612f62565b5060095460a060020a900460ff16151560000361195757604080518082018252600681527f31313039303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600c828154811061196a5761196a613423565b6000918252602082206003600490920201015460ff16151590036119d657604080518082018252600681527f31313039303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600c82815481106119e9576119e9613423565b90600052602060002090600402016002015460086000600c8581548110611a1257611a12613423565b6000918252602080832060016004909302019190910154600160a060020a0316835282019290925260400181208054909190611a4f908490613455565b9091555050600c805483908110611a6857611a68613423565b906000526020600020906004020160020154600d6000600c8581548110611a9157611a91613423565b60009182526020808320600490920290910154600160a060020a0316835282019290925260400181208054909190611aca908490613468565b925050819055506000600c8381548110611ae657611ae6613423565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817f6b50d6e4941ce7ae74ed7f7f51df5fb7680754c2815a094c66bea5ca80a40eb1600c8481548110611b4357611b43613423565b6000918252602090912060049091020154600c8054600160a060020a039092169186908110611b7457611b74613423565b6000918252602090912060016004909202010154604051611ba19291600160a060020a031690869061347b565b60405180910390a2600c8281548110611bbc57611bbc613423565b6000918252602090912060016004909202010154600c8054600160a060020a039092169184908110611bf057611bf0613423565b6000918252602090912060049091020154600c8054600160a060020a039092169160008051602061374a833981519152919086908110611c3257611c32613423565b906000526020600020906004020160020154604051611c5391815260200190565b60405180910390a35050565b600160a060020a031660009081526008602052604090205490565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611cc95760405160e560020a62461bcd02815260040161085a9190612f62565b506004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611d485760405160e560020a62461bcd02815260040161085a9190612f62565b506013805460ff191660011790556040517f8298c3671093b19970d7c94ce1f23925a962f36fec31d25075d9be072b73e10390600090a1565b60108054610ac2906134a7565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611ddd5760405160e560020a62461bcd02815260040161085a9190612f62565b5060066111198282613574565b600a602052600090815260409020805460018201546002830180549293919261178b906134a7565b60028054610ac2906134a7565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611e6e5760405160e560020a62461bcd02815260040161085a9190612f62565b5060148390556015611e808382613574565b506016611e8d8282613574565b506014546040517ff89b6034f2b759ebb2dfb08461be917a1b54f2e0523c895e7f9b5e5e14dd013e91611ec591601590601690613705565b60405180910390a1505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a03163314611f215760405160e560020a62461bcd02815260040161085a9190612f62565b506009805491151560a060020a0274ff000000000000000000000000000000000000000019909216919091179055565b600081611f5d33611c5f565b10156040518060400160405280600681526020017f313130343031000000000000000000000000000000000000000000000000000081525090611fb65760405160e560020a62461bcd02815260040161085a9190612f62565b5060095460408051808201909152600681527f313130343032000000000000000000000000000000000000000000000000000060208201529060a060020a900460ff16151560011461201e5760405160e560020a62461bcd02815260040161085a9190612f62565b506060833b1561203b57612033848483612ab9565b915050610e00565b612033848483612c07565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146120955760405160e560020a62461bcd02815260040161085a9190612f62565b5060056111198282613574565b600160a060020a039182166000908152600e6020908152604080832093909416825291909152205490565b816120d833866120a2565b101561212c57604080518082018252600681527f31313031303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61213a82610f6233876120a2565b336000908152600e60209081526040808320600160a060020a038916845290915290205561216b82610f8e85611c5f565b600160a060020a038085166000908152600860205260409081902092909255905133918616907f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc906121c2908790879087906134fd565b60405180910390a350505050565b816121da33611c5f565b101561222e57604080518082018252600681527f31313030303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61223b82610f6233611c5f565b3360008181526008602052604090209190915561225f908390610f8e9086906120a2565b600160a060020a0384166000818152600e602090815260408083203380855292529182902093909355519091907ecd77abc69e7bbb6de9df8cbfcca8720df3a30decf9eefd187e72ab2ea2f513906122ba9086908690613730565b60405180910390a3505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146123165760405160e560020a62461bcd02815260040161085a9190612f62565b506009805475ff00000000000000000000000000000000000000000019167501000000000000000000000000000000000000000000831515908102919091179091556040517fa0c691180b8089c0c84e478ee67d6325768666cd889f13de016771c6f7af1f8890600090a250565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146123d35760405160e560020a62461bcd02815260040161085a9190612f62565b50816123df86866120a2565b101561243357604080518082018252600681527f31313132303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61244182610f6287876120a2565b600160a060020a038087166000908152600e602090815260408083209389168352929052205561247482610f8e85611c5f565b6008600085600160a060020a0316600160a060020a031681526020019081526020016000208190555084600160a060020a031684600160a060020a03167f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc8585856040516124e4939291906134fd565b60405180910390a35050505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146125425760405160e560020a62461bcd02815260040161085a9190612f62565b50601255565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146125975760405160e560020a62461bcd02815260040161085a9190612f62565b5060106111198282613574565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146125f35760405160e560020a62461bcd02815260040161085a9190612f62565b50600160a060020a038216156126ab578061260e83856120a2565b101561266257604080518082018252600681527f31313131303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61267081610f6284866120a2565b600160a060020a038084166000908152600e60209081526040808320938816835292905220556003546126a39082612dfc565b600355612750565b600160a060020a03831660009081526008602052604090205481111561271957604080518082018252600681527f31313131303200000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b61272681610f6285611c5f565b600160a060020a03841660009081526008602052604090205560035461274c9082612dfc565b6003555b81600160a060020a031683600160a060020a031633600160a060020a03167fee02732fab40ece8284c756220846dff4b8d32058b86b35b4f0459bf172fcef08460405161279f91815260200190565b60405180910390a4505050565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146127fb5760405160e560020a62461bcd02815260040161085a9190612f62565b5060408051808201909152600681527f35303031303100000000000000000000000000000000000000000000000000006020820152600160a060020a03821661285a5760405160e560020a62461bcd02815260040161085a9190612f62565b5060008054604051600160a060020a03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146129125760405160e560020a62461bcd02815260040161085a9190612f62565b506009805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600c818154811061295257600080fd5b60009182526020909120600490910201805460018201546002830154600390930154600160a060020a0392831694509116919060ff1684565b600054604080518082019091526006815260d060020a6535303030303102602082015290600160a060020a031633146129da5760405160e560020a62461bcd02815260040161085a9190612f62565b50600160a060020a03821615612a33576129f881610f8e84866120a2565b600160a060020a038084166000908152600e6020908152604080832093881683529290522055600354612a2b9082612e0f565b600355612a6a565b612a4081610f8e85611c5f565b600160a060020a038416600090815260086020526040902055600354612a669082612e0f565b6003555b81600160a060020a031683600160a060020a031633600160a060020a03167f65adeb76912378393e600cb6f64f3310842a42e1eae86273dc775d0c47a0f2dc8460405161279f91815260200190565b60045460408051808201909152600681527f31313033303100000000000000000000000000000000000000000000000000006020820152600091600160a060020a03868116911614612b215760405160e560020a62461bcd02815260040161085a9190612f62565b50612b2f83610f6233611c5f565b33600090815260086020526040902055612b4c83610f8e86611c5f565b600160a060020a038516600081815260086020526040908190209290925590517fc0ee0b8a00000000000000000000000000000000000000000000000000000000815285919063c0ee0b8a90612baa903390889088906004016134fd565b600060405180830381600087803b158015612bc457600080fd5b505af1158015612bd8573d6000803e3d6000fd5b5050604051868152600160a060020a038816925033915060008051602061374a833981519152906020016110aa565b600454600090600160a060020a03163314801590612c2c5750600b5460ff1615156001145b15612c7f57604080518082018252600681527f31313032303100000000000000000000000000000000000000000000000000006020820152905160e560020a62461bcd02815261085a9190600401612f62565b600054600160a060020a03858116911614612d81576009546000546040517fd3da927f000000000000000000000000000000000000000000000000000000008152600160a060020a038781166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015612d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d25919061363a565b60408051808201909152600681527f31313032303200000000000000000000000000000000000000000000000000006020820152901515600114612d7f5760405160e560020a62461bcd02815260040161085a9190612f62565b505b612d8e83610f6233611c5f565b33600090815260086020526040902055612dab83610f8e86611c5f565b600160a060020a03851660008181526008602052604090819020929092559051339060008051602061374a83398151915290612dea9087815260200190565b60405180910390a35060019392505050565b6000612e088284613468565b9392505050565b6000612e088284613455565b60e060020a634e487b7102600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612e5d57612e5d612e1b565b604052919050565b600082601f830112612e7657600080fd5b813567ffffffffffffffff811115612e9057612e90612e1b565b612ea3601f8201601f1916602001612e34565b818152846020838601011115612eb857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215612ee857600080fd5b82359150602083013567ffffffffffffffff811115612f0657600080fd5b612f1285828601612e65565b9150509250929050565b6000815180845260005b81811015612f4257602081850181015186830182015201612f26565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612e086020830184612f1c565b600067ffffffffffffffff821115612f8f57612f8f612e1b565b5060209081020190565b8035600160a060020a0381168114612fb057600080fd5b919050565b600082601f830112612fc657600080fd5b81356020612fdb612fd683612f75565b612e34565b82815291810284018101918181019086841115612ff757600080fd5b8286015b848110156130125780358352918301918301612ffb565b509695505050505050565b6000806040838503121561303057600080fd5b823567ffffffffffffffff8082111561304857600080fd5b818501915085601f83011261305c57600080fd5b8135602061306c612fd683612f75565b8281529181028401810191818101908984111561308857600080fd5b948201945b838610156130ad5761309e86612f99565b8252948201949082019061308d565b965050860135925050808211156130c357600080fd5b50612f1285828601612fb5565b80151581146130de57600080fd5b50565b6000602082840312156130f357600080fd5b8135612e08816130d0565b60008060006060848603121561311357600080fd5b61311c84612f99565b925061312a60208501612f99565b9150604084013590509250925092565b60006020828403121561314c57600080fd5b813567ffffffffffffffff81111561316357600080fd5b61316f84828501612e65565b949350505050565b60006020828403121561318957600080fd5b612e0882612f99565b6000602082840312156131a457600080fd5b813560ff81168114612e0857600080fd5b600080604083850312156131c857600080fd5b6131d183612f99565b946020939093013593505050565b6000806000606084860312156131f457600080fd5b6131fd84612f99565b925060208401359150604084013567ffffffffffffffff81111561322057600080fd5b61322c86828701612e65565b9150509250925092565b83815260606020820152600061324f6060830185612f1c565b82810360408401526132618185612f1c565b9695505050505050565b83815282602082015260606040820152600061328a6060830184612f1c565b95945050505050565b6000806000606084860312156132a857600080fd5b83359250602084013567ffffffffffffffff808211156132c757600080fd5b6132d387838801612e65565b935060408601359150808211156132e957600080fd5b5061322c86828701612e65565b6000806040838503121561330957600080fd5b61331283612f99565b915061332060208401612f99565b90509250929050565b6000806000806080858703121561333f57600080fd5b61334885612f99565b935061335660208601612f99565b925060408501359150606085013567ffffffffffffffff81111561337957600080fd5b61338587828801612e65565b91505092959194509250565b600080600080600060a086880312156133a957600080fd5b6133b286612f99565b94506133c060208701612f99565b93506133ce60408701612f99565b925060608601359150608086013567ffffffffffffffff8111156133f157600080fd5b6133fd88828901612e65565b9150509295509295909350565b60006020828403121561341c57600080fd5b5035919050565b60e060020a634e487b7102600052603260045260246000fd5b60e060020a634e487b7102600052601160045260246000fd5b80820180821115610e0057610e0061343c565b81810381811115610e0057610e0061343c565b6000600160a060020a0380861683528085166020840152506060604083015261328a6060830184612f1c565b6002810460018216806134bb57607f821691505b6020821081036134de5760e060020a634e487b7102600052602260045260246000fd5b50919050565b6000600182016134f6576134f661343c565b5060010190565b600160a060020a038416815282602082015260606040820152600061328a6060830184612f1c565b601f82111561356f576000818152602081206020601f8601048101602086101561354c5750805b6020601f860104820191505b8181101561356b57828155600101613558565b5050505b505050565b815167ffffffffffffffff81111561358e5761358e612e1b565b6135a28161359c84546134a7565b84613525565b602080601f8311600181146135db57600084156135bf5750858301515b60028086026008870290910a600019041982161786555061356b565b600085815260208120601f198616915b8281101561360a578886015182559484019460019091019084016135eb565b508582101561362a57878501516008601f88160260020a60001904191681555b5050505050600202600101905550565b60006020828403121561364c57600080fd5b8151612e08816130d0565b6000600160a060020a038087168352808616602084015250836040830152608060608301526132616080830184612f1c565b60008154613696816134a7565b8085526020600183811680156136b357600181146136cc576136fa565b60ff1985168884015283151583028801830195506136fa565b866000528260002060005b858110156136f25781548a82018601529083019084016136d7565b890184019650505b505050505092915050565b83815260606020820152600061371e6060830185613689565b82810360408401526132618185613689565b82815260406020820152600061316f6040830184612f1c56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220333ee6f8177146f6409ff0926887d996f1ed72eb0e938e2d13cbbe5a5033c1b464736f6c63430008110033" + "bytecode": "60806040523480156200001157600080fd5b50604051620038be380380620038be8339810160408190526200003491620001d7565b600080546001600160a01b031916331790556001620000548a8262000370565b50600262000063898262000370565b50600080546001600160a01b03191633179055600f879055601281905560038690556014859055601562000098858262000370565b506016620000a7848262000370565b506010620000b6838262000370565b50506013805460ff199081169091556007805490911660011790555050600354600080546001600160a01b031681526008602052604090205550506009805460ff60a01b1916600160a01b179055506200043c92505050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200013757600080fd5b81516001600160401b03808211156200015457620001546200010f565b604051601f8301601f19908116603f011681019082821181831017156200017f576200017f6200010f565b81604052838152602092508660208588010111156200019d57600080fd5b600091505b83821015620001c15785820183015181830184015290820190620001a2565b6000602085830101528094505050505092915050565b60008060008060008060008060006101208a8c031215620001f757600080fd5b89516001600160401b03808211156200020f57600080fd5b6200021d8d838e0162000125565b9a5060208c01519150808211156200023457600080fd5b620002428d838e0162000125565b995060408c0151985060608c0151975060808c0151965060a08c01519150808211156200026e57600080fd5b6200027c8d838e0162000125565b955060c08c01519150808211156200029357600080fd5b620002a18d838e0162000125565b945060e08c0151915080821115620002b857600080fd5b50620002c78c828d0162000125565b9250506101008a015190509295985092959850929598565b600181811c90821680620002f457607f821691505b6020821081036200031557634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200036b576000816000526020600020601f850160051c81016020861015620003465750805b601f850160051c820191505b81811015620003675782815560010162000352565b5050505b505050565b81516001600160401b038111156200038c576200038c6200010f565b620003a4816200039d8454620002df565b846200031b565b602080601f831160018114620003dc5760008415620003c35750858301515b600019600386901b1c1916600185901b17855562000367565b600085815260208120601f198616915b828110156200040d57888601518255948401946001909101908401620003ec565b50858210156200042c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b613472806200044c6000396000f3fe608060405234801561001057600080fd5b50600436106103785760003560e01c806383850bcb116101d3578063c267ce5f11610104578063edaab208116100a2578063fd0e85691161007c578063fd0e85691461076b578063fd14a2221461077f578063fef1b9c014610792578063ff12dcea146107d257600080fd5b8063edaab20814610738578063f2fde38b14610745578063f7502a7c1461075857600080fd5b8063cb761015116100de578063cb761015146106d4578063d9322058146106e7578063db20266f146106fa578063e7464db11461072557600080fd5b8063c267ce5f1461069b578063c7109ec9146106ae578063ca5cbbb5146106c157600080fd5b8063996f3a4b11610171578063b2a90a601161014b578063b2a90a601461064e578063b967a52e14610662578063b9b3e06a14610675578063bfe30fb01461068857600080fd5b8063996f3a4b146106155780639cd2370714610628578063a9059cbb1461063b57600080fd5b80638ed8a9bc116101ad5780638ed8a9bc146105c457806392ff0d31146105d75780639301a78b146105eb57806395d89b411461060d57600080fd5b806383850bcb146105a15780638da5cb5b146105a95780638db9877c146105bc57600080fd5b806336f7ab5e116102ad5780635c40f6f41161024b57806370a082311161022557806370a082311461055257806376fa7a31146105655780637da68d341461057857806381ebdd791461058157600080fd5b80635c40f6f41461051f5780635ccef3e7146105325780636f3b993f1461054557600080fd5b8063428630f611610287578063428630f6146104ef57806358c3b870146104f75780635b473327146104ff5780635c2c20051461051657600080fd5b806336f7ab5e146104c157806340615cf8146104c957806340eba90e146104dc57600080fd5b8063200d2ed21161031a57806325d60861116102f457806325d608611461046857806327e235e31461047b5780632e027abe1461049b57806331cef447146104ae57600080fd5b8063200d2ed21461043557806323b872dd1461044257806325287d421461045557600080fd5b80630ed5a933116103565780630ed5a933146103db578063153a1f3e146103f857806318160ddd1461040b5780631935a8801461042257600080fd5b806306eaa0b71461037d57806306fdde03146103925780630af7eb0f146103b0575b600080fd5b61039061038b366004612bc3565b6107e5565b005b61039a610aaf565b6040516103a79190612c50565b60405180910390f35b6009546103c3906001600160a01b031681565b6040516001600160a01b0390911681526020016103a7565b6013546103e89060ff1681565b60405190151581526020016103a7565b6103e8610406366004612d12565b610b3d565b61041460035481565b6040519081526020016103a7565b610390610430366004612dd9565b610d84565b6007546103e89060ff1681565b6103e8610450366004612df6565b610e15565b6004546103c3906001600160a01b031681565b610390610476366004612e32565b610ffc565b610414610489366004612e6f565b60086020526000908152604090205481565b61039a6104a9366004612e8a565b611056565b6103906104bc366004612bc3565b61106f565b61039a6111ea565b6103906104d7366004612ead565b6111f7565b6103906104ea366004612ed7565b611295565b61039a61157c565b61039a611589565b610507611596565b6040516103a793929190612f2e565b610414600f5481565b61039061052d366004612dd9565b6116bb565b610390610540366004612bc3565b61174a565b600b546103e89060ff1681565b610414610560366004612e6f565b611acc565b610390610573366004612e6f565b611ae7565b61041460125481565b61041461058f366004612e6f565b600d6020526000908152604090205481565b610390611b53565b6000546103c3906001600160a01b031681565b61039a611bd5565b6103906105d2366004612e32565b611be2565b6009546103e890600160a81b900460ff1681565b6105fe6105f9366004612e6f565b611c38565b6040516103a793929190612f63565b61039a611c60565b610390610623366004612f8b565b611c6d565b610390610636366004612dd9565b611d1a565b6103e8610649366004612ead565b611d82565b6009546103e890600160b01b900460ff1681565b610390610670366004612e32565b611e43565b610414610683366004612fee565b611e99565b610390610696366004613021565b611ec4565b6103906106a9366004612ed7565b611fad565b6103906106bc366004612dd9565b61208a565b6103906106cf366004613089565b61211d565b6103906106e2366004613102565b61226c565b6103906106f5366004612e32565b6122bb565b610414610708366004612fee565b600e60209081526000928352604080842090915290825290205481565b610390610733366004612df6565b612311565b6017546103e89060ff1681565b610390610753366004612e6f565b6124df565b610390610766366004612e6f565b6125c9565b6009546103e890600160a01b900460ff1681565b61039061078d366004612dd9565b612635565b6107a56107a0366004613102565b61269d565b604080516001600160a01b03958616815294909316602085015291830152151560608201526080016103a7565b6103906107e0366004612df6565b6126e6565b336001600160a01b0316600c83815481106108025761080261311b565b60009182526020909120600490910201546001600160a01b03161480159061083557506000546001600160a01b03163314155b1561087757604080518082018252600681526531313038303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b60405180910390fd5b600c828154811061088a5761088a61311b565b6000918252602082206003600490920201015460ff16151590036108dc5760408051808201825260068152651898981c181960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b600c82815481106108ef576108ef61311b565b90600052602060002090600402016002015460086000600c85815481106109185761091861311b565b600091825260208083206004909202909101546001600160a01b0316835282019290925260400181208054909190610951908490613147565b9091555050600c80548390811061096a5761096a61311b565b906000526020600020906004020160020154600d6000600c85815481106109935761099361311b565b600091825260208083206004909202909101546001600160a01b03168352820192909252604001812080549091906109cc90849061315a565b925050819055506000600c83815481106109e8576109e861311b565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817fa0cc3a4feb26502e6e6cd57b2113d58bdab80c6711d8252356be9a49945bb5f7600c8481548110610a4557610a4561311b565b6000918252602090912060049091020154600c80546001600160a01b039092169186908110610a7657610a7661311b565b6000918252602090912060016004909202010154604051610aa392916001600160a01b031690869061316d565b60405180910390a25050565b60018054610abc90613199565b80601f0160208091040260200160405190810160405280929190818152602001828054610ae890613199565b8015610b355780601f10610b0a57610100808354040283529160200191610b35565b820191906000526020600020905b815481529060010190602001808311610b1857829003601f168201915b505050505081565b600b5460009060ff161515600103610b8357604080518082018252600681526531313035303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b8151835114610bc05760408051808201825260068152651898981a981960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b6000805b8451811015610bfc57838181518110610bdf57610bdf61311b565b602002602001015182610bf29190613147565b9150600101610bc4565b5080610c0733611acc565b1015610c4157604080518082018252600681526531313035303360d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6004546001600160a01b03163314610ca2576009546040805180820190915260068152650c4c4c0d4c0d60d21b602082015290600160a81b900460ff161515600114610ca05760405162461bcd60e51b815260040161086e9190612c50565b505b606060006001935060005b8651811015610d7957610cd9878281518110610ccb57610ccb61311b565b60200260200101513b151590565b15610d2257610d1b878281518110610cf357610cf361311b565b6020026020010151878381518110610d0d57610d0d61311b565b60200260200101518561280e565b9150610d62565b610d5f878281518110610d3757610d3761311b565b6020026020010151878381518110610d5157610d5161311b565b602002602001015185612929565b91505b811515600003610d7157600094505b600101610cad565b505050505b92915050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610dcd5760405162461bcd60e51b815260040161086e9190612c50565b50600b805460ff19168215159081179091556040519081527fe1455bc53682229469d6919bb96d7b338cbffea93170dfb5f02242c22fb6d07f9060200160405180910390a150565b6000805460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610e5f5760405162461bcd60e51b815260040161086e9190612c50565b5081610e6a85611acc565b1015610ea457604080518082018252600681526531313036303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6060833b15610f6957610ec083610eba87611acc565b90612aed565b6001600160a01b038616600090815260086020526040902055610eec83610ee686611acc565b90612b00565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a90610f31903390889087906004016131d3565b600060405180830381600087803b158015610f4b57600080fd5b505af1158015610f5f573d6000803e3d6000fd5b5050505050610fb6565b610f7683610eba87611acc565b6001600160a01b038616600090815260086020526040902055610f9c83610ee686611acc565b6001600160a01b0385166000908152600860205260409020555b836001600160a01b0316856001600160a01b031660008051602061341d83398151915285604051610fe991815260200190565b60405180910390a3506001949350505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146110455760405162461bcd60e51b815260040161086e9190612c50565b506011611052828261324b565b5050565b60186020526000908152604090208054610abc90613199565b60095460408051808201909152600681526531313130303160d01b602082015290600160b01b900460ff1615156001146110bc5760405162461bcd60e51b815260040161086e9190612c50565b50600954600160a01b900460ff16151560010361118b5760095460005460405163d3da927f60e01b81523360048201526001600160a01b03918216602482015291169063d3da927f90604401602060405180830381865afa158015611125573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611149919061330b565b60408051808201909152600681526518989898181960d11b60208201529015156001146111895760405162461bcd60e51b815260040161086e9190612c50565b505b336000908152600a602052604090208281556002016111aa828261324b565b507f3bb9553f633a7ecb4cbc5f9885e750a522496ce06deff1ed9184180ca83940c03383836040516111de939291906131d3565b60405180910390a15050565b60058054610abc90613199565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146112405760405162461bcd60e51b815260040161086e9190612c50565b506001600160a01b0382166000818152600a6020908152604091829020600101849055815192835282018390527fa42bd6bdfcb8f9b815c68e4ce0e16dec3f1e7ac53967ee8309653de522a735b191016111de565b600b5460ff1615806112b15750600954600160a81b900460ff16155b806112c35750816112c133611acc565b105b156112fc57604080518082018252600681526531313037303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6000546001600160a01b038481169116148015906113285750600954600160a01b900460ff1615156001145b156113e75760095460005460405163d3da927f60e01b81526001600160a01b038681166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015611381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a5919061330b565b6040805180820190915260068152651898981b981960d11b60208201529015156001146113e55760405162461bcd60e51b815260040161086e9190612c50565b505b336000908152600860205260408120805484929061140690849061315a565b9091555050336000908152600d60205260408120805484929061142a908490613147565b9091555050600c805460408051608081018252338082526001600160a01b038881166020840190815283850189815260016060860181815290880189556000989098529351600487027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7810180549285166001600160a01b031993841617905591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8830180549190941691161790915591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c983015593517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca909101805491151560ff1990921691909117905551909182917fa256c3bc72c26fbfd6285cfde8c7e345b4d0c7ccfc725d103e9f129c5d8a039a9161156e91889088908890613328565b60405180910390a250505050565b60068054610abc90613199565b60118054610abc90613199565b60148054601580549192916115aa90613199565b80601f01602080910402602001604051908101604052809291908181526020018280546115d690613199565b80156116235780601f106115f857610100808354040283529160200191611623565b820191906000526020600020905b81548152906001019060200180831161160657829003601f168201915b50505050509080600201805461163890613199565b80601f016020809104026020016040519081016040528092919081815260200182805461166490613199565b80156116b15780601f10611686576101008083540402835291602001916116b1565b820191906000526020600020905b81548152906001019060200180831161169457829003601f168201915b5050505050905083565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146117045760405162461bcd60e51b815260040161086e9190612c50565b506007805460ff191682151590811790915560405160ff9091161515907fe1e5c6f5867805bf3fab73f4df4b80d0e87e9262a7bc22ac7c2cb3fd5896222f90600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146117935760405162461bcd60e51b815260040161086e9190612c50565b50600954600160a81b900460ff1615156000036117de57604080518082018252600681526531313039303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b600c82815481106117f1576117f161311b565b6000918252602082206003600490920201015460ff16151590036118435760408051808201825260068152651898981c981960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b600c82815481106118565761185661311b565b90600052602060002090600402016002015460086000600c858154811061187f5761187f61311b565b60009182526020808320600160049093020191909101546001600160a01b03168352820192909252604001812080549091906118bc908490613147565b9091555050600c8054839081106118d5576118d561311b565b906000526020600020906004020160020154600d6000600c85815481106118fe576118fe61311b565b600091825260208083206004909202909101546001600160a01b031683528201929092526040018120805490919061193790849061315a565b925050819055506000600c83815481106119535761195361311b565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817f6b50d6e4941ce7ae74ed7f7f51df5fb7680754c2815a094c66bea5ca80a40eb1600c84815481106119b0576119b061311b565b6000918252602090912060049091020154600c80546001600160a01b0390921691869081106119e1576119e161311b565b6000918252602090912060016004909202010154604051611a0e92916001600160a01b031690869061316d565b60405180910390a2600c8281548110611a2957611a2961311b565b6000918252602090912060016004909202010154600c80546001600160a01b039092169184908110611a5d57611a5d61311b565b6000918252602090912060049091020154600c80546001600160a01b039092169160008051602061341d833981519152919086908110611a9f57611a9f61311b565b906000526020600020906004020160020154604051611ac091815260200190565b60405180910390a35050565b6001600160a01b031660009081526008602052604090205490565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b305760405162461bcd60e51b815260040161086e9190612c50565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b9c5760405162461bcd60e51b815260040161086e9190612c50565b506013805460ff191660011790556040517f8298c3671093b19970d7c94ce1f23925a962f36fec31d25075d9be072b73e10390600090a1565b60108054610abc90613199565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c2b5760405162461bcd60e51b815260040161086e9190612c50565b506006611052828261324b565b600a602052600090815260409020805460018201546002830180549293919261163890613199565b60028054610abc90613199565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611cb65760405162461bcd60e51b815260040161086e9190612c50565b5060148390556015611cc8838261324b565b506016611cd5828261324b565b506014546040517ff89b6034f2b759ebb2dfb08461be917a1b54f2e0523c895e7f9b5e5e14dd013e91611d0d916015906016906133d8565b60405180910390a1505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611d635760405162461bcd60e51b815260040161086e9190612c50565b5060098054911515600160a81b0260ff60a81b19909216919091179055565b600081611d8e33611acc565b10156040518060400160405280600681526020016531313034303160d01b81525090611dcd5760405162461bcd60e51b815260040161086e9190612c50565b506009546040805180820190915260068152651898981a181960d11b602082015290600160a81b900460ff161515600114611e1b5760405162461bcd60e51b815260040161086e9190612c50565b506060833b15611e3857611e3084848361280e565b915050610d7e565b611e30848483612929565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611e8c5760405162461bcd60e51b815260040161086e9190612c50565b506005611052828261324b565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205490565b81611ecf3386611e99565b1015611f0957604080518082018252600681526518989818981960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b611f1782610eba3387611e99565b336000908152600e602090815260408083206001600160a01b0389168452909152902055611f4882610ee685611acc565b6001600160a01b038085166000908152600860205260409081902092909255905133918616907f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc90611f9f908790879087906131d3565b60405180910390a350505050565b81611fb733611acc565b1015611ff157604080518082018252600681526518989818181960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b611ffe82610eba33611acc565b33600081815260086020526040902091909155612022908390610ee6908690611e99565b6001600160a01b0384166000818152600e602090815260408083203380855292529182902093909355519091907ecd77abc69e7bbb6de9df8cbfcca8720df3a30decf9eefd187e72ab2ea2f5139061207d9086908690613403565b60405180910390a3505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146120d35760405162461bcd60e51b815260040161086e9190612c50565b506009805460ff60b01b1916600160b01b831515908102919091179091556040517fa0c691180b8089c0c84e478ee67d6325768666cd889f13de016771c6f7af1f8890600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146121665760405162461bcd60e51b815260040161086e9190612c50565b50816121728686611e99565b10156121ac57604080518082018252600681526531313132303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6121ba82610eba8787611e99565b6001600160a01b038087166000908152600e60209081526040808320938916835292905220556121ed82610ee685611acc565b60086000856001600160a01b03166001600160a01b0316815260200190815260200160002081905550846001600160a01b0316846001600160a01b03167f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc85858560405161225d939291906131d3565b60405180910390a35050505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146122b55760405162461bcd60e51b815260040161086e9190612c50565b50601255565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146123045760405162461bcd60e51b815260040161086e9190612c50565b506010611052828261324b565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461235a5760405162461bcd60e51b815260040161086e9190612c50565b506001600160a01b038216156123f857806123758385611e99565b10156123af57604080518082018252600681526531313131303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6123bd81610eba8486611e99565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546123f09082612aed565b600355612483565b6001600160a01b03831660009081526008602052604090205481111561244c57604080518082018252600681526518989898981960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b61245981610eba85611acc565b6001600160a01b03841660009081526008602052604090205560035461247f9082612aed565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167fee02732fab40ece8284c756220846dff4b8d32058b86b35b4f0459bf172fcef0846040516124d291815260200190565b60405180910390a4505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146125285760405162461bcd60e51b815260040161086e9190612c50565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b03821661256d5760405162461bcd60e51b815260040161086e9190612c50565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146126125760405162461bcd60e51b815260040161086e9190612c50565b50600980546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461267e5760405162461bcd60e51b815260040161086e9190612c50565b5060098054911515600160a01b0260ff60a01b19909216919091179055565b600c81815481106126ad57600080fd5b600091825260209091206004909102018054600182015460028301546003909301546001600160a01b0392831694509116919060ff1684565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461272f5760405162461bcd60e51b815260040161086e9190612c50565b506001600160a01b038216156127885761274d81610ee68486611e99565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546127809082612b00565b6003556127bf565b61279581610ee685611acc565b6001600160a01b0384166000908152600860205260409020556003546127bb9082612b00565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167f65adeb76912378393e600cb6f64f3310842a42e1eae86273dc775d0c47a0f2dc846040516124d291815260200190565b60045460408051808201909152600681526531313033303160d01b60208201526000916001600160a01b0386811691161461285c5760405162461bcd60e51b815260040161086e9190612c50565b5061286a83610eba33611acc565b3360009081526008602052604090205561288783610ee686611acc565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a906128cc903390889088906004016131d3565b600060405180830381600087803b1580156128e657600080fd5b505af11580156128fa573d6000803e3d6000fd5b50506040518681526001600160a01b038816925033915060008051602061341d83398151915290602001610fe9565b6004546000906001600160a01b0316331480159061294e5750600b5460ff1615156001145b1561298757604080518082018252600681526531313032303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6000546001600160a01b038581169116148015906129b35750600954600160a01b900460ff1615156001145b15612a725760095460005460405163d3da927f60e01b81526001600160a01b038781166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015612a0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a30919061330b565b60408051808201909152600681526518989819181960d11b6020820152901515600114612a705760405162461bcd60e51b815260040161086e9190612c50565b505b612a7f83610eba33611acc565b33600090815260086020526040902055612a9c83610ee686611acc565b6001600160a01b03851660008181526008602052604090819020929092559051339060008051602061341d83398151915290612adb9087815260200190565b60405180910390a35060019392505050565b6000612af9828461315a565b9392505050565b6000612af98284613147565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612b4b57612b4b612b0c565b604052919050565b600082601f830112612b6457600080fd5b813567ffffffffffffffff811115612b7e57612b7e612b0c565b612b91601f8201601f1916602001612b22565b818152846020838601011115612ba657600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215612bd657600080fd5b82359150602083013567ffffffffffffffff811115612bf457600080fd5b612c0085828601612b53565b9150509250929050565b6000815180845260005b81811015612c3057602081850181015186830182015201612c14565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612af96020830184612c0a565b600067ffffffffffffffff821115612c7d57612c7d612b0c565b5060051b60200190565b80356001600160a01b0381168114612c9e57600080fd5b919050565b600082601f830112612cb457600080fd5b81356020612cc9612cc483612c63565b612b22565b8083825260208201915060208460051b870101935086841115612ceb57600080fd5b602086015b84811015612d075780358352918301918301612cf0565b509695505050505050565b60008060408385031215612d2557600080fd5b823567ffffffffffffffff80821115612d3d57600080fd5b818501915085601f830112612d5157600080fd5b81356020612d61612cc483612c63565b82815260059290921b84018101918181019089841115612d8057600080fd5b948201945b83861015612da557612d9686612c87565b82529482019490820190612d85565b96505086013592505080821115612dbb57600080fd5b50612c0085828601612ca3565b8015158114612dd657600080fd5b50565b600060208284031215612deb57600080fd5b8135612af981612dc8565b600080600060608486031215612e0b57600080fd5b612e1484612c87565b9250612e2260208501612c87565b9150604084013590509250925092565b600060208284031215612e4457600080fd5b813567ffffffffffffffff811115612e5b57600080fd5b612e6784828501612b53565b949350505050565b600060208284031215612e8157600080fd5b612af982612c87565b600060208284031215612e9c57600080fd5b813560ff81168114612af957600080fd5b60008060408385031215612ec057600080fd5b612ec983612c87565b946020939093013593505050565b600080600060608486031215612eec57600080fd5b612ef584612c87565b925060208401359150604084013567ffffffffffffffff811115612f1857600080fd5b612f2486828701612b53565b9150509250925092565b838152606060208201526000612f476060830185612c0a565b8281036040840152612f598185612c0a565b9695505050505050565b838152826020820152606060408201526000612f826060830184612c0a565b95945050505050565b600080600060608486031215612fa057600080fd5b83359250602084013567ffffffffffffffff80821115612fbf57600080fd5b612fcb87838801612b53565b93506040860135915080821115612fe157600080fd5b50612f2486828701612b53565b6000806040838503121561300157600080fd5b61300a83612c87565b915061301860208401612c87565b90509250929050565b6000806000806080858703121561303757600080fd5b61304085612c87565b935061304e60208601612c87565b925060408501359150606085013567ffffffffffffffff81111561307157600080fd5b61307d87828801612b53565b91505092959194509250565b600080600080600060a086880312156130a157600080fd5b6130aa86612c87565b94506130b860208701612c87565b93506130c660408701612c87565b925060608601359150608086013567ffffffffffffffff8111156130e957600080fd5b6130f588828901612b53565b9150509295509295909350565b60006020828403121561311457600080fd5b5035919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610d7e57610d7e613131565b81810381811115610d7e57610d7e613131565b6001600160a01b03848116825283166020820152606060408201819052600090612f8290830184612c0a565b600181811c908216806131ad57607f821691505b6020821081036131cd57634e487b7160e01b600052602260045260246000fd5b50919050565b60018060a01b0384168152826020820152606060408201526000612f826060830184612c0a565b601f821115613246576000816000526020600020601f850160051c810160208610156132235750805b601f850160051c820191505b818110156132425782815560010161322f565b5050505b505050565b815167ffffffffffffffff81111561326557613265612b0c565b613279816132738454613199565b846131fa565b602080601f8311600181146132ae57600084156132965750858301515b600019600386901b1c1916600185901b178555613242565b600085815260208120601f198616915b828110156132dd578886015182559484019460019091019084016132be565b50858210156132fb5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561331d57600080fd5b8151612af981612dc8565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612f5990830184612c0a565b6000815461336881613199565b808552602060018381168015613385576001811461339f576133cd565b60ff1985168884015283151560051b8801830195506133cd565b866000528260002060005b858110156133c55781548a82018601529083019084016133aa565b890184019650505b505050505092915050565b8381526060602082015260006133f1606083018561335b565b8281036040840152612f59818561335b565b828152604060208201526000612e676040830184612c0a56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212201889db3f768c3826c5ce92020d70f772bf2f23adad1ce1774ab7ed5a27c863ee64736f6c63430008170033", + "deployedBytecode": "608060405234801561001057600080fd5b50600436106103785760003560e01c806383850bcb116101d3578063c267ce5f11610104578063edaab208116100a2578063fd0e85691161007c578063fd0e85691461076b578063fd14a2221461077f578063fef1b9c014610792578063ff12dcea146107d257600080fd5b8063edaab20814610738578063f2fde38b14610745578063f7502a7c1461075857600080fd5b8063cb761015116100de578063cb761015146106d4578063d9322058146106e7578063db20266f146106fa578063e7464db11461072557600080fd5b8063c267ce5f1461069b578063c7109ec9146106ae578063ca5cbbb5146106c157600080fd5b8063996f3a4b11610171578063b2a90a601161014b578063b2a90a601461064e578063b967a52e14610662578063b9b3e06a14610675578063bfe30fb01461068857600080fd5b8063996f3a4b146106155780639cd2370714610628578063a9059cbb1461063b57600080fd5b80638ed8a9bc116101ad5780638ed8a9bc146105c457806392ff0d31146105d75780639301a78b146105eb57806395d89b411461060d57600080fd5b806383850bcb146105a15780638da5cb5b146105a95780638db9877c146105bc57600080fd5b806336f7ab5e116102ad5780635c40f6f41161024b57806370a082311161022557806370a082311461055257806376fa7a31146105655780637da68d341461057857806381ebdd791461058157600080fd5b80635c40f6f41461051f5780635ccef3e7146105325780636f3b993f1461054557600080fd5b8063428630f611610287578063428630f6146104ef57806358c3b870146104f75780635b473327146104ff5780635c2c20051461051657600080fd5b806336f7ab5e146104c157806340615cf8146104c957806340eba90e146104dc57600080fd5b8063200d2ed21161031a57806325d60861116102f457806325d608611461046857806327e235e31461047b5780632e027abe1461049b57806331cef447146104ae57600080fd5b8063200d2ed21461043557806323b872dd1461044257806325287d421461045557600080fd5b80630ed5a933116103565780630ed5a933146103db578063153a1f3e146103f857806318160ddd1461040b5780631935a8801461042257600080fd5b806306eaa0b71461037d57806306fdde03146103925780630af7eb0f146103b0575b600080fd5b61039061038b366004612bc3565b6107e5565b005b61039a610aaf565b6040516103a79190612c50565b60405180910390f35b6009546103c3906001600160a01b031681565b6040516001600160a01b0390911681526020016103a7565b6013546103e89060ff1681565b60405190151581526020016103a7565b6103e8610406366004612d12565b610b3d565b61041460035481565b6040519081526020016103a7565b610390610430366004612dd9565b610d84565b6007546103e89060ff1681565b6103e8610450366004612df6565b610e15565b6004546103c3906001600160a01b031681565b610390610476366004612e32565b610ffc565b610414610489366004612e6f565b60086020526000908152604090205481565b61039a6104a9366004612e8a565b611056565b6103906104bc366004612bc3565b61106f565b61039a6111ea565b6103906104d7366004612ead565b6111f7565b6103906104ea366004612ed7565b611295565b61039a61157c565b61039a611589565b610507611596565b6040516103a793929190612f2e565b610414600f5481565b61039061052d366004612dd9565b6116bb565b610390610540366004612bc3565b61174a565b600b546103e89060ff1681565b610414610560366004612e6f565b611acc565b610390610573366004612e6f565b611ae7565b61041460125481565b61041461058f366004612e6f565b600d6020526000908152604090205481565b610390611b53565b6000546103c3906001600160a01b031681565b61039a611bd5565b6103906105d2366004612e32565b611be2565b6009546103e890600160a81b900460ff1681565b6105fe6105f9366004612e6f565b611c38565b6040516103a793929190612f63565b61039a611c60565b610390610623366004612f8b565b611c6d565b610390610636366004612dd9565b611d1a565b6103e8610649366004612ead565b611d82565b6009546103e890600160b01b900460ff1681565b610390610670366004612e32565b611e43565b610414610683366004612fee565b611e99565b610390610696366004613021565b611ec4565b6103906106a9366004612ed7565b611fad565b6103906106bc366004612dd9565b61208a565b6103906106cf366004613089565b61211d565b6103906106e2366004613102565b61226c565b6103906106f5366004612e32565b6122bb565b610414610708366004612fee565b600e60209081526000928352604080842090915290825290205481565b610390610733366004612df6565b612311565b6017546103e89060ff1681565b610390610753366004612e6f565b6124df565b610390610766366004612e6f565b6125c9565b6009546103e890600160a01b900460ff1681565b61039061078d366004612dd9565b612635565b6107a56107a0366004613102565b61269d565b604080516001600160a01b03958616815294909316602085015291830152151560608201526080016103a7565b6103906107e0366004612df6565b6126e6565b336001600160a01b0316600c83815481106108025761080261311b565b60009182526020909120600490910201546001600160a01b03161480159061083557506000546001600160a01b03163314155b1561087757604080518082018252600681526531313038303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b60405180910390fd5b600c828154811061088a5761088a61311b565b6000918252602082206003600490920201015460ff16151590036108dc5760408051808201825260068152651898981c181960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b600c82815481106108ef576108ef61311b565b90600052602060002090600402016002015460086000600c85815481106109185761091861311b565b600091825260208083206004909202909101546001600160a01b0316835282019290925260400181208054909190610951908490613147565b9091555050600c80548390811061096a5761096a61311b565b906000526020600020906004020160020154600d6000600c85815481106109935761099361311b565b600091825260208083206004909202909101546001600160a01b03168352820192909252604001812080549091906109cc90849061315a565b925050819055506000600c83815481106109e8576109e861311b565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817fa0cc3a4feb26502e6e6cd57b2113d58bdab80c6711d8252356be9a49945bb5f7600c8481548110610a4557610a4561311b565b6000918252602090912060049091020154600c80546001600160a01b039092169186908110610a7657610a7661311b565b6000918252602090912060016004909202010154604051610aa392916001600160a01b031690869061316d565b60405180910390a25050565b60018054610abc90613199565b80601f0160208091040260200160405190810160405280929190818152602001828054610ae890613199565b8015610b355780601f10610b0a57610100808354040283529160200191610b35565b820191906000526020600020905b815481529060010190602001808311610b1857829003601f168201915b505050505081565b600b5460009060ff161515600103610b8357604080518082018252600681526531313035303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b8151835114610bc05760408051808201825260068152651898981a981960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b6000805b8451811015610bfc57838181518110610bdf57610bdf61311b565b602002602001015182610bf29190613147565b9150600101610bc4565b5080610c0733611acc565b1015610c4157604080518082018252600681526531313035303360d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6004546001600160a01b03163314610ca2576009546040805180820190915260068152650c4c4c0d4c0d60d21b602082015290600160a81b900460ff161515600114610ca05760405162461bcd60e51b815260040161086e9190612c50565b505b606060006001935060005b8651811015610d7957610cd9878281518110610ccb57610ccb61311b565b60200260200101513b151590565b15610d2257610d1b878281518110610cf357610cf361311b565b6020026020010151878381518110610d0d57610d0d61311b565b60200260200101518561280e565b9150610d62565b610d5f878281518110610d3757610d3761311b565b6020026020010151878381518110610d5157610d5161311b565b602002602001015185612929565b91505b811515600003610d7157600094505b600101610cad565b505050505b92915050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610dcd5760405162461bcd60e51b815260040161086e9190612c50565b50600b805460ff19168215159081179091556040519081527fe1455bc53682229469d6919bb96d7b338cbffea93170dfb5f02242c22fb6d07f9060200160405180910390a150565b6000805460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610e5f5760405162461bcd60e51b815260040161086e9190612c50565b5081610e6a85611acc565b1015610ea457604080518082018252600681526531313036303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6060833b15610f6957610ec083610eba87611acc565b90612aed565b6001600160a01b038616600090815260086020526040902055610eec83610ee686611acc565b90612b00565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a90610f31903390889087906004016131d3565b600060405180830381600087803b158015610f4b57600080fd5b505af1158015610f5f573d6000803e3d6000fd5b5050505050610fb6565b610f7683610eba87611acc565b6001600160a01b038616600090815260086020526040902055610f9c83610ee686611acc565b6001600160a01b0385166000908152600860205260409020555b836001600160a01b0316856001600160a01b031660008051602061341d83398151915285604051610fe991815260200190565b60405180910390a3506001949350505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146110455760405162461bcd60e51b815260040161086e9190612c50565b506011611052828261324b565b5050565b60186020526000908152604090208054610abc90613199565b60095460408051808201909152600681526531313130303160d01b602082015290600160b01b900460ff1615156001146110bc5760405162461bcd60e51b815260040161086e9190612c50565b50600954600160a01b900460ff16151560010361118b5760095460005460405163d3da927f60e01b81523360048201526001600160a01b03918216602482015291169063d3da927f90604401602060405180830381865afa158015611125573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611149919061330b565b60408051808201909152600681526518989898181960d11b60208201529015156001146111895760405162461bcd60e51b815260040161086e9190612c50565b505b336000908152600a602052604090208281556002016111aa828261324b565b507f3bb9553f633a7ecb4cbc5f9885e750a522496ce06deff1ed9184180ca83940c03383836040516111de939291906131d3565b60405180910390a15050565b60058054610abc90613199565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146112405760405162461bcd60e51b815260040161086e9190612c50565b506001600160a01b0382166000818152600a6020908152604091829020600101849055815192835282018390527fa42bd6bdfcb8f9b815c68e4ce0e16dec3f1e7ac53967ee8309653de522a735b191016111de565b600b5460ff1615806112b15750600954600160a81b900460ff16155b806112c35750816112c133611acc565b105b156112fc57604080518082018252600681526531313037303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6000546001600160a01b038481169116148015906113285750600954600160a01b900460ff1615156001145b156113e75760095460005460405163d3da927f60e01b81526001600160a01b038681166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015611381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a5919061330b565b6040805180820190915260068152651898981b981960d11b60208201529015156001146113e55760405162461bcd60e51b815260040161086e9190612c50565b505b336000908152600860205260408120805484929061140690849061315a565b9091555050336000908152600d60205260408120805484929061142a908490613147565b9091555050600c805460408051608081018252338082526001600160a01b038881166020840190815283850189815260016060860181815290880189556000989098529351600487027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7810180549285166001600160a01b031993841617905591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8830180549190941691161790915591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c983015593517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca909101805491151560ff1990921691909117905551909182917fa256c3bc72c26fbfd6285cfde8c7e345b4d0c7ccfc725d103e9f129c5d8a039a9161156e91889088908890613328565b60405180910390a250505050565b60068054610abc90613199565b60118054610abc90613199565b60148054601580549192916115aa90613199565b80601f01602080910402602001604051908101604052809291908181526020018280546115d690613199565b80156116235780601f106115f857610100808354040283529160200191611623565b820191906000526020600020905b81548152906001019060200180831161160657829003601f168201915b50505050509080600201805461163890613199565b80601f016020809104026020016040519081016040528092919081815260200182805461166490613199565b80156116b15780601f10611686576101008083540402835291602001916116b1565b820191906000526020600020905b81548152906001019060200180831161169457829003601f168201915b5050505050905083565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146117045760405162461bcd60e51b815260040161086e9190612c50565b506007805460ff191682151590811790915560405160ff9091161515907fe1e5c6f5867805bf3fab73f4df4b80d0e87e9262a7bc22ac7c2cb3fd5896222f90600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146117935760405162461bcd60e51b815260040161086e9190612c50565b50600954600160a81b900460ff1615156000036117de57604080518082018252600681526531313039303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b600c82815481106117f1576117f161311b565b6000918252602082206003600490920201015460ff16151590036118435760408051808201825260068152651898981c981960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b600c82815481106118565761185661311b565b90600052602060002090600402016002015460086000600c858154811061187f5761187f61311b565b60009182526020808320600160049093020191909101546001600160a01b03168352820192909252604001812080549091906118bc908490613147565b9091555050600c8054839081106118d5576118d561311b565b906000526020600020906004020160020154600d6000600c85815481106118fe576118fe61311b565b600091825260208083206004909202909101546001600160a01b031683528201929092526040018120805490919061193790849061315a565b925050819055506000600c83815481106119535761195361311b565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817f6b50d6e4941ce7ae74ed7f7f51df5fb7680754c2815a094c66bea5ca80a40eb1600c84815481106119b0576119b061311b565b6000918252602090912060049091020154600c80546001600160a01b0390921691869081106119e1576119e161311b565b6000918252602090912060016004909202010154604051611a0e92916001600160a01b031690869061316d565b60405180910390a2600c8281548110611a2957611a2961311b565b6000918252602090912060016004909202010154600c80546001600160a01b039092169184908110611a5d57611a5d61311b565b6000918252602090912060049091020154600c80546001600160a01b039092169160008051602061341d833981519152919086908110611a9f57611a9f61311b565b906000526020600020906004020160020154604051611ac091815260200190565b60405180910390a35050565b6001600160a01b031660009081526008602052604090205490565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b305760405162461bcd60e51b815260040161086e9190612c50565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b9c5760405162461bcd60e51b815260040161086e9190612c50565b506013805460ff191660011790556040517f8298c3671093b19970d7c94ce1f23925a962f36fec31d25075d9be072b73e10390600090a1565b60108054610abc90613199565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c2b5760405162461bcd60e51b815260040161086e9190612c50565b506006611052828261324b565b600a602052600090815260409020805460018201546002830180549293919261163890613199565b60028054610abc90613199565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611cb65760405162461bcd60e51b815260040161086e9190612c50565b5060148390556015611cc8838261324b565b506016611cd5828261324b565b506014546040517ff89b6034f2b759ebb2dfb08461be917a1b54f2e0523c895e7f9b5e5e14dd013e91611d0d916015906016906133d8565b60405180910390a1505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611d635760405162461bcd60e51b815260040161086e9190612c50565b5060098054911515600160a81b0260ff60a81b19909216919091179055565b600081611d8e33611acc565b10156040518060400160405280600681526020016531313034303160d01b81525090611dcd5760405162461bcd60e51b815260040161086e9190612c50565b506009546040805180820190915260068152651898981a181960d11b602082015290600160a81b900460ff161515600114611e1b5760405162461bcd60e51b815260040161086e9190612c50565b506060833b15611e3857611e3084848361280e565b915050610d7e565b611e30848483612929565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611e8c5760405162461bcd60e51b815260040161086e9190612c50565b506005611052828261324b565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205490565b81611ecf3386611e99565b1015611f0957604080518082018252600681526518989818981960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b611f1782610eba3387611e99565b336000908152600e602090815260408083206001600160a01b0389168452909152902055611f4882610ee685611acc565b6001600160a01b038085166000908152600860205260409081902092909255905133918616907f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc90611f9f908790879087906131d3565b60405180910390a350505050565b81611fb733611acc565b1015611ff157604080518082018252600681526518989818181960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b611ffe82610eba33611acc565b33600081815260086020526040902091909155612022908390610ee6908690611e99565b6001600160a01b0384166000818152600e602090815260408083203380855292529182902093909355519091907ecd77abc69e7bbb6de9df8cbfcca8720df3a30decf9eefd187e72ab2ea2f5139061207d9086908690613403565b60405180910390a3505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146120d35760405162461bcd60e51b815260040161086e9190612c50565b506009805460ff60b01b1916600160b01b831515908102919091179091556040517fa0c691180b8089c0c84e478ee67d6325768666cd889f13de016771c6f7af1f8890600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146121665760405162461bcd60e51b815260040161086e9190612c50565b50816121728686611e99565b10156121ac57604080518082018252600681526531313132303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6121ba82610eba8787611e99565b6001600160a01b038087166000908152600e60209081526040808320938916835292905220556121ed82610ee685611acc565b60086000856001600160a01b03166001600160a01b0316815260200190815260200160002081905550846001600160a01b0316846001600160a01b03167f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc85858560405161225d939291906131d3565b60405180910390a35050505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146122b55760405162461bcd60e51b815260040161086e9190612c50565b50601255565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146123045760405162461bcd60e51b815260040161086e9190612c50565b506010611052828261324b565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461235a5760405162461bcd60e51b815260040161086e9190612c50565b506001600160a01b038216156123f857806123758385611e99565b10156123af57604080518082018252600681526531313131303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6123bd81610eba8486611e99565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546123f09082612aed565b600355612483565b6001600160a01b03831660009081526008602052604090205481111561244c57604080518082018252600681526518989898981960d11b6020820152905162461bcd60e51b815261086e9190600401612c50565b61245981610eba85611acc565b6001600160a01b03841660009081526008602052604090205560035461247f9082612aed565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167fee02732fab40ece8284c756220846dff4b8d32058b86b35b4f0459bf172fcef0846040516124d291815260200190565b60405180910390a4505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146125285760405162461bcd60e51b815260040161086e9190612c50565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b03821661256d5760405162461bcd60e51b815260040161086e9190612c50565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146126125760405162461bcd60e51b815260040161086e9190612c50565b50600980546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461267e5760405162461bcd60e51b815260040161086e9190612c50565b5060098054911515600160a01b0260ff60a01b19909216919091179055565b600c81815481106126ad57600080fd5b600091825260209091206004909102018054600182015460028301546003909301546001600160a01b0392831694509116919060ff1684565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461272f5760405162461bcd60e51b815260040161086e9190612c50565b506001600160a01b038216156127885761274d81610ee68486611e99565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546127809082612b00565b6003556127bf565b61279581610ee685611acc565b6001600160a01b0384166000908152600860205260409020556003546127bb9082612b00565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167f65adeb76912378393e600cb6f64f3310842a42e1eae86273dc775d0c47a0f2dc846040516124d291815260200190565b60045460408051808201909152600681526531313033303160d01b60208201526000916001600160a01b0386811691161461285c5760405162461bcd60e51b815260040161086e9190612c50565b5061286a83610eba33611acc565b3360009081526008602052604090205561288783610ee686611acc565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a906128cc903390889088906004016131d3565b600060405180830381600087803b1580156128e657600080fd5b505af11580156128fa573d6000803e3d6000fd5b50506040518681526001600160a01b038816925033915060008051602061341d83398151915290602001610fe9565b6004546000906001600160a01b0316331480159061294e5750600b5460ff1615156001145b1561298757604080518082018252600681526531313032303160d01b6020820152905162461bcd60e51b815261086e9190600401612c50565b6000546001600160a01b038581169116148015906129b35750600954600160a01b900460ff1615156001145b15612a725760095460005460405163d3da927f60e01b81526001600160a01b038781166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015612a0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a30919061330b565b60408051808201909152600681526518989819181960d11b6020820152901515600114612a705760405162461bcd60e51b815260040161086e9190612c50565b505b612a7f83610eba33611acc565b33600090815260086020526040902055612a9c83610ee686611acc565b6001600160a01b03851660008181526008602052604090819020929092559051339060008051602061341d83398151915290612adb9087815260200190565b60405180910390a35060019392505050565b6000612af9828461315a565b9392505050565b6000612af98284613147565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612b4b57612b4b612b0c565b604052919050565b600082601f830112612b6457600080fd5b813567ffffffffffffffff811115612b7e57612b7e612b0c565b612b91601f8201601f1916602001612b22565b818152846020838601011115612ba657600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215612bd657600080fd5b82359150602083013567ffffffffffffffff811115612bf457600080fd5b612c0085828601612b53565b9150509250929050565b6000815180845260005b81811015612c3057602081850181015186830182015201612c14565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612af96020830184612c0a565b600067ffffffffffffffff821115612c7d57612c7d612b0c565b5060051b60200190565b80356001600160a01b0381168114612c9e57600080fd5b919050565b600082601f830112612cb457600080fd5b81356020612cc9612cc483612c63565b612b22565b8083825260208201915060208460051b870101935086841115612ceb57600080fd5b602086015b84811015612d075780358352918301918301612cf0565b509695505050505050565b60008060408385031215612d2557600080fd5b823567ffffffffffffffff80821115612d3d57600080fd5b818501915085601f830112612d5157600080fd5b81356020612d61612cc483612c63565b82815260059290921b84018101918181019089841115612d8057600080fd5b948201945b83861015612da557612d9686612c87565b82529482019490820190612d85565b96505086013592505080821115612dbb57600080fd5b50612c0085828601612ca3565b8015158114612dd657600080fd5b50565b600060208284031215612deb57600080fd5b8135612af981612dc8565b600080600060608486031215612e0b57600080fd5b612e1484612c87565b9250612e2260208501612c87565b9150604084013590509250925092565b600060208284031215612e4457600080fd5b813567ffffffffffffffff811115612e5b57600080fd5b612e6784828501612b53565b949350505050565b600060208284031215612e8157600080fd5b612af982612c87565b600060208284031215612e9c57600080fd5b813560ff81168114612af957600080fd5b60008060408385031215612ec057600080fd5b612ec983612c87565b946020939093013593505050565b600080600060608486031215612eec57600080fd5b612ef584612c87565b925060208401359150604084013567ffffffffffffffff811115612f1857600080fd5b612f2486828701612b53565b9150509250925092565b838152606060208201526000612f476060830185612c0a565b8281036040840152612f598185612c0a565b9695505050505050565b838152826020820152606060408201526000612f826060830184612c0a565b95945050505050565b600080600060608486031215612fa057600080fd5b83359250602084013567ffffffffffffffff80821115612fbf57600080fd5b612fcb87838801612b53565b93506040860135915080821115612fe157600080fd5b50612f2486828701612b53565b6000806040838503121561300157600080fd5b61300a83612c87565b915061301860208401612c87565b90509250929050565b6000806000806080858703121561303757600080fd5b61304085612c87565b935061304e60208601612c87565b925060408501359150606085013567ffffffffffffffff81111561307157600080fd5b61307d87828801612b53565b91505092959194509250565b600080600080600060a086880312156130a157600080fd5b6130aa86612c87565b94506130b860208701612c87565b93506130c660408701612c87565b925060608601359150608086013567ffffffffffffffff8111156130e957600080fd5b6130f588828901612b53565b9150509295509295909350565b60006020828403121561311457600080fd5b5035919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610d7e57610d7e613131565b81810381811115610d7e57610d7e613131565b6001600160a01b03848116825283166020820152606060408201819052600090612f8290830184612c0a565b600181811c908216806131ad57607f821691505b6020821081036131cd57634e487b7160e01b600052602260045260246000fd5b50919050565b60018060a01b0384168152826020820152606060408201526000612f826060830184612c0a565b601f821115613246576000816000526020600020601f850160051c810160208610156132235750805b601f850160051c820191505b818110156132425782815560010161322f565b5050505b505050565b815167ffffffffffffffff81111561326557613265612b0c565b613279816132738454613199565b846131fa565b602080601f8311600181146132ae57600084156132965750858301515b600019600386901b1c1916600185901b178555613242565b600085815260208120601f198616915b828110156132dd578886015182559484019460019091019084016132be565b50858210156132fb5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561331d57600080fd5b8151612af981612dc8565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612f5990830184612c0a565b6000815461336881613199565b808552602060018381168015613385576001811461339f576133cd565b60ff1985168884015283151560051b8801830195506133cd565b866000528260002060005b858110156133c55781548a82018601529083019084016133aa565b890184019650505b505050505092915050565b8381526060602082015260006133f1606083018561335b565b8281036040840152612f59818561335b565b828152604060208201526000612e676040830184612c0a56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212201889db3f768c3826c5ce92020d70f772bf2f23adad1ce1774ab7ed5a27c863ee64736f6c63430008170033" } \ No newline at end of file diff --git a/contracts/IbetStraightBond.json b/contracts/IbetStraightBond.json index 25541faa..d437a9ee 100644 --- a/contracts/IbetStraightBond.json +++ b/contracts/IbetStraightBond.json @@ -1092,6 +1092,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "requirePersonalInfoRegistered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "returnAmount", @@ -1274,6 +1287,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_requireRegistered", + "type": "bool" + } + ], + "name": "setRequirePersonalInfoRegistered", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1499,6 +1525,6 @@ "type": "function" } ], - "bytecode": "60806040523480156200001157600080fd5b5060405162003918380380620039188339810160408190526200003491620001e2565b600080546001600160a01b0319908116339182161717905560016200005a8c826200041e565b506002620000698b826200041e565b506003899055600f88905560196200008288826200041e565b5060126200009187826200041e565b506013859055601b620000a585826200041e565b506014620000b484826200041e565b506015620000c383826200041e565b506016620000d282826200041e565b5050600354600080546001600160a01b031681526008602052604090205550506018805460ff1990811690915560078054909116600117905550620004ea9650505050505050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200014257600080fd5b81516001600160401b03808211156200015f576200015f6200011a565b604051601f8301601f19908116603f011681019082821181831017156200018a576200018a6200011a565b8160405283815260209250866020858801011115620001a857600080fd5b600091505b83821015620001cc5785820183015181830184015290820190620001ad565b6000602085830101528094505050505092915050565b60008060008060008060008060008060006101608c8e0312156200020557600080fd5b8b516001600160401b038111156200021c57600080fd5b6200022a8e828f0162000130565b60208e0151909c5090506001600160401b038111156200024957600080fd5b620002578e828f0162000130565b9a505060408c0151985060608c0151975060808c015160018060401b038111156200028157600080fd5b6200028f8e828f0162000130565b60a08e015190985090506001600160401b03811115620002ae57600080fd5b620002bc8e828f0162000130565b60c08e015160e08f0151919850965090506001600160401b03811115620002e257600080fd5b620002f08e828f0162000130565b6101008e015190955090506001600160401b038111156200031057600080fd5b6200031e8e828f0162000130565b6101208e015190945090506001600160401b038111156200033e57600080fd5b6200034c8e828f0162000130565b6101408e015190935090506001600160401b038111156200036c57600080fd5b6200037a8e828f0162000130565b9150509295989b509295989b9093969950565b600181811c90821680620003a257607f821691505b602082108103620003c357634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000419576000816000526020600020601f850160051c81016020861015620003f45750805b601f850160051c820191505b81811015620004155782815560010162000400565b5050505b505050565b81516001600160401b038111156200043a576200043a6200011a565b62000452816200044b84546200038d565b84620003c9565b602080601f8311600181146200048a5760008415620004715750858301515b600019600386901b1c1916600185901b17855562000415565b600085815260208120601f198616915b82811015620004bb578886015182559484019460019091019084016200049a565b5085821015620004da5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61341e80620004fa6000396000f3fe608060405234801561001057600080fd5b50600436106103db5760003560e01c806376fa7a311161020a578063b9b3e06a11610125578063ed62c2a0116100b8578063f35eeaef11610087578063f35eeaef14610818578063f7502a7c14610820578063fc590d0914610833578063fef1b9c01461083b578063ff12dcea1461087b57600080fd5b8063ed62c2a0146107e1578063ef4474cd146107e9578063ef8032ef146107f2578063f2fde38b1461080557600080fd5b8063ca5cbbb5116100f4578063ca5cbbb514610788578063d5d9d0c71461079b578063db20266f146107a3578063e7464db1146107ce57600080fd5b8063b9b3e06a1461073c578063bfe30fb01461074f578063c267ce5f14610762578063c7109ec91461077557600080fd5b80639301a78b1161019d578063a9059cbb1161016c578063a9059cbb146106ef578063ab62f92e14610702578063b2a90a6014610715578063b967a52e1461072957600080fd5b80639301a78b1461069f57806395d89b41146106c157806397e4c513146106c95780639cd23707146106dc57600080fd5b80638da5cb5b116101d95780638da5cb5b146106525780638ed8a9bc14610665578063919fe52d1461067857806392ff0d311461068b57600080fd5b806376fa7a31146106035780637c3a00fd1461061657806381ebdd791461061f5780638ac08bf11461063f57600080fd5b80633a2b6939116102fa5780635c40f6f41161028d5780636666e49c1161025c5780636666e49c146105d35780636f3b993f146105db57806370740aab146105e857806370a08231146105f057600080fd5b80635c40f6f4146105875780635ccef3e71461059a5780635f84f302146105ad57806363783444146105c057600080fd5b8063428630f6116102c9578063428630f61461056657806344fd9caa1461056e57806358c3b8701461057757806359d936171461057f57600080fd5b80633a2b6939146105255780633b18b9841461052d57806340615cf81461054057806340eba90e1461055357600080fd5b806323b872dd116103725780632e0ad004116103415780632e0ad004146104fa57806331cef44714610502578063329dbddc1461051557806336f7ab5e1461051d57600080fd5b806323b872dd146104a157806325287d42146104b457806325d60861146104c757806327e235e3146104da57600080fd5b8063153a1f3e116103ae578063153a1f3e1461045757806318160ddd1461046a5780631935a88014610481578063200d2ed21461049457600080fd5b8063034f6b21146103e057806306eaa0b71461040257806306fdde03146104175780630af7eb0f1461042c575b600080fd5b6018546103ed9060ff1681565b60405190151581526020015b60405180910390f35b610415610410366004612cc8565b61088e565b005b61041f610b58565b6040516103f99190612d55565b60095461043f906001600160a01b031681565b6040516001600160a01b0390911681526020016103f9565b6103ed610465366004612e17565b610be6565b61047360035481565b6040519081526020016103f9565b61041561048f366004612ede565b610de9565b6007546103ed9060ff1681565b6103ed6104af366004612efb565b610e7b565b60045461043f906001600160a01b031681565b6104156104d5366004612f37565b611062565b6104736104e8366004612f74565b60086020526000908152604090205481565b61041f6110bc565b610415610510366004612cc8565b6110c9565b61041f61122d565b61041f61123a565b61041f611247565b61041561053b366004612f8f565b611254565b61041561054e366004612fa8565b6112d3565b610415610561366004612fd2565b611371565b61041f61163c565b610473600f5481565b61041f611649565b61041f611656565b610415610595366004612ede565b611663565b6104156105a8366004612cc8565b6116f2565b6104156105bb366004612f8f565b611a74565b6104156105ce366004612f37565b611ac3565b61041f611b19565b600b546103ed9060ff1681565b61041f611b26565b6104736105fe366004612f74565b611b33565b610415610611366004612f74565b611b4e565b61047360105481565b61047361062d366004612f74565b600d6020526000908152604090205481565b61041561064d366004612f37565b611bba565b60005461043f906001600160a01b031681565b610415610673366004612f37565b611c10565b610415610686366004612f37565b611c66565b6009546103ed90600160a01b900460ff1681565b6106b26106ad366004612f74565b611cbc565b6040516103f993929190613029565b61041f611d67565b6104156106d7366004612f37565b611d74565b6104156106ea366004612ede565b611dca565b6103ed6106fd366004612fa8565b611e32565b610415610710366004612f37565b611ef3565b6009546103ed90600160a81b900460ff1681565b610415610737366004612f37565b611f49565b61047361074a366004613051565b611f9f565b61041561075d366004613084565b611fca565b610415610770366004612fd2565b6120b3565b610415610783366004612ede565b612190565b6104156107963660046130ec565b612223565b610415612372565b6104736107b1366004613051565b600e60209081526000928352604080842090915290825290205481565b6104156107dc366004612efb565b6123f4565b61041f6125c2565b61047360135481565b610415610800366004612f8f565b6125cf565b610415610813366004612f74565b61264e565b61041f612738565b61041561082e366004612f74565b612745565b61041f6127b1565b61084e610849366004612f8f565b6127be565b604080516001600160a01b03958616815294909316602085015291830152151560608201526080016103f9565b610415610889366004612efb565b612807565b336001600160a01b0316600c83815481106108ab576108ab613165565b60009182526020909120600490910201546001600160a01b0316148015906108de57506000546001600160a01b03163314155b1561092057604080518082018252600681526531323038303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b60405180910390fd5b600c828154811061093357610933613165565b6000918252602082206003600490920201015460ff16151590036109855760408051808201825260068152651899181c181960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b600c828154811061099857610998613165565b90600052602060002090600402016002015460086000600c85815481106109c1576109c1613165565b600091825260208083206004909202909101546001600160a01b03168352820192909252604001812080549091906109fa908490613191565b9091555050600c805483908110610a1357610a13613165565b906000526020600020906004020160020154600d6000600c8581548110610a3c57610a3c613165565b600091825260208083206004909202909101546001600160a01b0316835282019290925260400181208054909190610a759084906131a4565b925050819055506000600c8381548110610a9157610a91613165565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817fa0cc3a4feb26502e6e6cd57b2113d58bdab80c6711d8252356be9a49945bb5f7600c8481548110610aee57610aee613165565b6000918252602090912060049091020154600c80546001600160a01b039092169186908110610b1f57610b1f613165565b6000918252602090912060016004909202010154604051610b4c92916001600160a01b03169086906131b7565b60405180910390a25050565b60018054610b65906131e3565b80601f0160208091040260200160405190810160405280929190818152602001828054610b91906131e3565b8015610bde5780601f10610bb357610100808354040283529160200191610bde565b820191906000526020600020905b815481529060010190602001808311610bc157829003601f168201915b505050505081565b60008151835114610c2557604080518082018252600681526531323035303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6000805b8451811015610c6157838181518110610c4457610c44613165565b602002602001015182610c579190613191565b9150600101610c29565b5080610c6c33611b33565b1015610ca65760408051808201825260068152651899181a981960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b6004546001600160a01b03163314610d075760095460408051808201909152600681526531323035303360d01b602082015290600160a01b900460ff161515600114610d055760405162461bcd60e51b81526004016109179190612d55565b505b606060006001935060005b8651811015610dde57610d3e878281518110610d3057610d30613165565b60200260200101513b151590565b15610d8757610d80878281518110610d5857610d58613165565b6020026020010151878381518110610d7257610d72613165565b60200260200101518561292f565b9150610dc7565b610dc4878281518110610d9c57610d9c613165565b6020026020010151878381518110610db657610db6613165565b602002602001015185612a4a565b91505b811515600003610dd657600094505b600101610d12565b505050505b92915050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610e325760405162461bcd60e51b81526004016109179190612d55565b50600b805460ff19168215159081179091556040519081527fe1455bc53682229469d6919bb96d7b338cbffea93170dfb5f02242c22fb6d07f906020015b60405180910390a150565b6000805460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610ec55760405162461bcd60e51b81526004016109179190612d55565b5081610ed085611b33565b1015610f0a57604080518082018252600681526531323036303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6060833b15610fcf57610f2683610f2087611b33565b90612bf2565b6001600160a01b038616600090815260086020526040902055610f5283610f4c86611b33565b90612c05565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a90610f979033908890879060040161321d565b600060405180830381600087803b158015610fb157600080fd5b505af1158015610fc5573d6000803e3d6000fd5b505050505061101c565b610fdc83610f2087611b33565b6001600160a01b03861660009081526008602052604090205561100283610f4c86611b33565b6001600160a01b0385166000908152600860205260409020555b836001600160a01b0316856001600160a01b03166000805160206133c98339815191528560405161104f91815260200190565b60405180910390a3506001949350505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146110ab5760405162461bcd60e51b81526004016109179190612d55565b5060176110b88282613295565b5050565b601a8054610b65906131e3565b60095460408051808201909152600681526531323130303160d01b602082015290600160a81b900460ff1615156001146111165760405162461bcd60e51b81526004016109179190612d55565b5060095460005460405163d3da927f60e01b81523360048201526001600160a01b03918216602482015291169063d3da927f90604401602060405180830381865afa158015611169573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118d9190613355565b60408051808201909152600681526518991898181960d11b60208201529015156001146111cd5760405162461bcd60e51b81526004016109179190612d55565b50336000908152600a602052604090208281556002016111ed8282613295565b507f3bb9553f633a7ecb4cbc5f9885e750a522496ce06deff1ed9184180ca83940c03383836040516112219392919061321d565b60405180910390a15050565b60118054610b65906131e3565b60058054610b65906131e3565b60198054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461129d5760405162461bcd60e51b81526004016109179190612d55565b5060138190556040518181527fe451abe602c33e612ea61221b5a142f7fe3c044286e42c8340ffe7fa86a5859790602001610e70565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461131c5760405162461bcd60e51b81526004016109179190612d55565b506001600160a01b0382166000818152600a6020908152604091829020600101849055815192835282018390527fa42bd6bdfcb8f9b815c68e4ce0e16dec3f1e7ac53967ee8309653de522a735b19101611221565b600b5460ff16158061138d5750600954600160a01b900460ff16155b8061139f57508161139d33611b33565b105b156113d857604080518082018252600681526531323037303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6000546001600160a01b038481169116146114a75760095460005460405163d3da927f60e01b81526001600160a01b038681166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015611441573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114659190613355565b6040805180820190915260068152651899181b981960d11b60208201529015156001146114a55760405162461bcd60e51b81526004016109179190612d55565b505b33600090815260086020526040812080548492906114c69084906131a4565b9091555050336000908152600d6020526040812080548492906114ea908490613191565b9091555050600c805460408051608081018252338082526001600160a01b038881166020840190815283850189815260016060860181815290880189556000989098529351600487027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7810180549285166001600160a01b031993841617905591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8830180549190941691161790915591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c983015593517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca909101805491151560ff1990921691909117905551909182917fa256c3bc72c26fbfd6285cfde8c7e345b4d0c7ccfc725d103e9f129c5d8a039a9161162e91889088908890613372565b60405180910390a250505050565b60068054610b65906131e3565b60178054610b65906131e3565b601b8054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146116ac5760405162461bcd60e51b81526004016109179190612d55565b506007805460ff191682151590811790915560405160ff9091161515907fe1e5c6f5867805bf3fab73f4df4b80d0e87e9262a7bc22ac7c2cb3fd5896222f90600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461173b5760405162461bcd60e51b81526004016109179190612d55565b50600954600160a01b900460ff16151560000361178657604080518082018252600681526531323039303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b600c828154811061179957611799613165565b6000918252602082206003600490920201015460ff16151590036117eb5760408051808201825260068152651899181c981960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b600c82815481106117fe576117fe613165565b90600052602060002090600402016002015460086000600c858154811061182757611827613165565b60009182526020808320600160049093020191909101546001600160a01b0316835282019290925260400181208054909190611864908490613191565b9091555050600c80548390811061187d5761187d613165565b906000526020600020906004020160020154600d6000600c85815481106118a6576118a6613165565b600091825260208083206004909202909101546001600160a01b03168352820192909252604001812080549091906118df9084906131a4565b925050819055506000600c83815481106118fb576118fb613165565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817f6b50d6e4941ce7ae74ed7f7f51df5fb7680754c2815a094c66bea5ca80a40eb1600c848154811061195857611958613165565b6000918252602090912060049091020154600c80546001600160a01b03909216918690811061198957611989613165565b60009182526020909120600160049092020101546040516119b692916001600160a01b03169086906131b7565b60405180910390a2600c82815481106119d1576119d1613165565b6000918252602090912060016004909202010154600c80546001600160a01b039092169184908110611a0557611a05613165565b6000918252602090912060049091020154600c80546001600160a01b03909216916000805160206133c9833981519152919086908110611a4757611a47613165565b906000526020600020906004020160020154604051611a6891815260200190565b60405180910390a35050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611abd5760405162461bcd60e51b81526004016109179190612d55565b50601055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b0c5760405162461bcd60e51b81526004016109179190612d55565b5060116110b88282613295565b60128054610b65906131e3565b60168054610b65906131e3565b6001600160a01b031660009081526008602052604090205490565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b975760405162461bcd60e51b81526004016109179190612d55565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c035760405162461bcd60e51b81526004016109179190612d55565b5060196110b88282613295565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c595760405162461bcd60e51b81526004016109179190612d55565b5060066110b88282613295565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611caf5760405162461bcd60e51b81526004016109179190612d55565b50601b6110b88282613295565b600a6020526000908152604090208054600182015460028301805492939192611ce4906131e3565b80601f0160208091040260200160405190810160405280929190818152602001828054611d10906131e3565b8015611d5d5780601f10611d3257610100808354040283529160200191611d5d565b820191906000526020600020905b815481529060010190602001808311611d4057829003601f168201915b5050505050905083565b60028054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611dbd5760405162461bcd60e51b81526004016109179190612d55565b50601c6110b88282613295565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611e135760405162461bcd60e51b81526004016109179190612d55565b5060098054911515600160a01b0260ff60a01b19909216919091179055565b600081611e3e33611b33565b10156040518060400160405280600681526020016531323034303160d01b81525090611e7d5760405162461bcd60e51b81526004016109179190612d55565b506009546040805180820190915260068152651899181a181960d11b602082015290600160a01b900460ff161515600114611ecb5760405162461bcd60e51b81526004016109179190612d55565b506060833b15611ee857611ee084848361292f565b915050610de3565b611ee0848483612a4a565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611f3c5760405162461bcd60e51b81526004016109179190612d55565b50601a6110b88282613295565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611f925760405162461bcd60e51b81526004016109179190612d55565b5060056110b88282613295565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205490565b81611fd53386611f9f565b101561200f57604080518082018252600681526518991818981960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b61201d82610f203387611f9f565b336000908152600e602090815260408083206001600160a01b038916845290915290205561204e82610f4c85611b33565b6001600160a01b038085166000908152600860205260409081902092909255905133918616907f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc906120a59087908790879061321d565b60405180910390a350505050565b816120bd33611b33565b10156120f757604080518082018252600681526518991818181960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b61210482610f2033611b33565b33600081815260086020526040902091909155612128908390610f4c908690611f9f565b6001600160a01b0384166000818152600e602090815260408083203380855292529182902093909355519091907ecd77abc69e7bbb6de9df8cbfcca8720df3a30decf9eefd187e72ab2ea2f5139061218390869086906133af565b60405180910390a3505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146121d95760405162461bcd60e51b81526004016109179190612d55565b506009805460ff60a81b1916600160a81b831515908102919091179091556040517fa0c691180b8089c0c84e478ee67d6325768666cd889f13de016771c6f7af1f8890600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461226c5760405162461bcd60e51b81526004016109179190612d55565b50816122788686611f9f565b10156122b257604080518082018252600681526531323132303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6122c082610f208787611f9f565b6001600160a01b038087166000908152600e60209081526040808320938916835292905220556122f382610f4c85611b33565b60086000856001600160a01b03166001600160a01b0316815260200190815260200160002081905550846001600160a01b0316846001600160a01b03167f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc8585856040516123639392919061321d565b60405180910390a35050505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146123bb5760405162461bcd60e51b81526004016109179190612d55565b506018805460ff191660011790556040517f96151d7f1d8c50be29a45c0e3ae9ce2008888d1615b196e8f5ffd8c178e6dde890600090a1565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461243d5760405162461bcd60e51b81526004016109179190612d55565b506001600160a01b038216156124db57806124588385611f9f565b101561249257604080518082018252600681526531323131303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6124a081610f208486611f9f565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546124d39082612bf2565b600355612566565b6001600160a01b03831660009081526008602052604090205481111561252f57604080518082018252600681526518991898981960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b61253c81610f2085611b33565b6001600160a01b0384166000908152600860205260409020556003546125629082612bf2565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167fee02732fab40ece8284c756220846dff4b8d32058b86b35b4f0459bf172fcef0846040516125b591815260200190565b60405180910390a4505050565b601c8054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146126185760405162461bcd60e51b81526004016109179190612d55565b50600f8190556040518181527f82b19c9a00131a87eeff1903fcb9e4f2a3ba76dd86098586eea921a533e218dc90602001610e70565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146126975760405162461bcd60e51b81526004016109179190612d55565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b0382166126dc5760405162461bcd60e51b81526004016109179190612d55565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60158054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461278e5760405162461bcd60e51b81526004016109179190612d55565b50600980546001600160a01b0319166001600160a01b0392909216919091179055565b60148054610b65906131e3565b600c81815481106127ce57600080fd5b600091825260209091206004909102018054600182015460028301546003909301546001600160a01b0392831694509116919060ff1684565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146128505760405162461bcd60e51b81526004016109179190612d55565b506001600160a01b038216156128a95761286e81610f4c8486611f9f565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546128a19082612c05565b6003556128e0565b6128b681610f4c85611b33565b6001600160a01b0384166000908152600860205260409020556003546128dc9082612c05565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167f65adeb76912378393e600cb6f64f3310842a42e1eae86273dc775d0c47a0f2dc846040516125b591815260200190565b60045460408051808201909152600681526531323033303160d01b60208201526000916001600160a01b0386811691161461297d5760405162461bcd60e51b81526004016109179190612d55565b5061298b83610f2033611b33565b336000908152600860205260409020556129a883610f4c86611b33565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a906129ed9033908890889060040161321d565b600060405180830381600087803b158015612a0757600080fd5b505af1158015612a1b573d6000803e3d6000fd5b50506040518681526001600160a01b03881692503391506000805160206133c98339815191529060200161104f565b6004546000906001600160a01b03163314801590612a6f5750600b5460ff1615156001145b15612aa857604080518082018252600681526531323032303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6000546001600160a01b03858116911614612b775760095460005460405163d3da927f60e01b81526001600160a01b038781166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015612b11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b359190613355565b60408051808201909152600681526518991819181960d11b6020820152901515600114612b755760405162461bcd60e51b81526004016109179190612d55565b505b612b8483610f2033611b33565b33600090815260086020526040902055612ba183610f4c86611b33565b6001600160a01b0385166000818152600860205260409081902092909255905133906000805160206133c983398151915290612be09087815260200190565b60405180910390a35060019392505050565b6000612bfe82846131a4565b9392505050565b6000612bfe8284613191565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612c5057612c50612c11565b604052919050565b600082601f830112612c6957600080fd5b813567ffffffffffffffff811115612c8357612c83612c11565b612c96601f8201601f1916602001612c27565b818152846020838601011115612cab57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215612cdb57600080fd5b82359150602083013567ffffffffffffffff811115612cf957600080fd5b612d0585828601612c58565b9150509250929050565b6000815180845260005b81811015612d3557602081850181015186830182015201612d19565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612bfe6020830184612d0f565b600067ffffffffffffffff821115612d8257612d82612c11565b5060051b60200190565b80356001600160a01b0381168114612da357600080fd5b919050565b600082601f830112612db957600080fd5b81356020612dce612dc983612d68565b612c27565b8083825260208201915060208460051b870101935086841115612df057600080fd5b602086015b84811015612e0c5780358352918301918301612df5565b509695505050505050565b60008060408385031215612e2a57600080fd5b823567ffffffffffffffff80821115612e4257600080fd5b818501915085601f830112612e5657600080fd5b81356020612e66612dc983612d68565b82815260059290921b84018101918181019089841115612e8557600080fd5b948201945b83861015612eaa57612e9b86612d8c565b82529482019490820190612e8a565b96505086013592505080821115612ec057600080fd5b50612d0585828601612da8565b8015158114612edb57600080fd5b50565b600060208284031215612ef057600080fd5b8135612bfe81612ecd565b600080600060608486031215612f1057600080fd5b612f1984612d8c565b9250612f2760208501612d8c565b9150604084013590509250925092565b600060208284031215612f4957600080fd5b813567ffffffffffffffff811115612f6057600080fd5b612f6c84828501612c58565b949350505050565b600060208284031215612f8657600080fd5b612bfe82612d8c565b600060208284031215612fa157600080fd5b5035919050565b60008060408385031215612fbb57600080fd5b612fc483612d8c565b946020939093013593505050565b600080600060608486031215612fe757600080fd5b612ff084612d8c565b925060208401359150604084013567ffffffffffffffff81111561301357600080fd5b61301f86828701612c58565b9150509250925092565b8381528260208201526060604082015260006130486060830184612d0f565b95945050505050565b6000806040838503121561306457600080fd5b61306d83612d8c565b915061307b60208401612d8c565b90509250929050565b6000806000806080858703121561309a57600080fd5b6130a385612d8c565b93506130b160208601612d8c565b925060408501359150606085013567ffffffffffffffff8111156130d457600080fd5b6130e087828801612c58565b91505092959194509250565b600080600080600060a0868803121561310457600080fd5b61310d86612d8c565b945061311b60208701612d8c565b935061312960408701612d8c565b925060608601359150608086013567ffffffffffffffff81111561314c57600080fd5b61315888828901612c58565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610de357610de361317b565b81810381811115610de357610de361317b565b6001600160a01b0384811682528316602082015260606040820181905260009061304890830184612d0f565b600181811c908216806131f757607f821691505b60208210810361321757634e487b7160e01b600052602260045260246000fd5b50919050565b60018060a01b03841681528260208201526060604082015260006130486060830184612d0f565b601f821115613290576000816000526020600020601f850160051c8101602086101561326d5750805b601f850160051c820191505b8181101561328c57828155600101613279565b5050505b505050565b815167ffffffffffffffff8111156132af576132af612c11565b6132c3816132bd84546131e3565b84613244565b602080601f8311600181146132f857600084156132e05750858301515b600019600386901b1c1916600185901b17855561328c565b600085815260208120601f198616915b8281101561332757888601518255948401946001909101908401613308565b50858210156133455787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561336757600080fd5b8151612bfe81612ecd565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906133a590830184612d0f565b9695505050505050565b828152604060208201526000612f6c6040830184612d0f56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122088f29532258abd280c78b38a51851d03be753cbc68978a3c5f7fbc262f37bfa964736f6c63430008170033", - "deployedBytecode": "608060405234801561001057600080fd5b50600436106103db5760003560e01c806376fa7a311161020a578063b9b3e06a11610125578063ed62c2a0116100b8578063f35eeaef11610087578063f35eeaef14610818578063f7502a7c14610820578063fc590d0914610833578063fef1b9c01461083b578063ff12dcea1461087b57600080fd5b8063ed62c2a0146107e1578063ef4474cd146107e9578063ef8032ef146107f2578063f2fde38b1461080557600080fd5b8063ca5cbbb5116100f4578063ca5cbbb514610788578063d5d9d0c71461079b578063db20266f146107a3578063e7464db1146107ce57600080fd5b8063b9b3e06a1461073c578063bfe30fb01461074f578063c267ce5f14610762578063c7109ec91461077557600080fd5b80639301a78b1161019d578063a9059cbb1161016c578063a9059cbb146106ef578063ab62f92e14610702578063b2a90a6014610715578063b967a52e1461072957600080fd5b80639301a78b1461069f57806395d89b41146106c157806397e4c513146106c95780639cd23707146106dc57600080fd5b80638da5cb5b116101d95780638da5cb5b146106525780638ed8a9bc14610665578063919fe52d1461067857806392ff0d311461068b57600080fd5b806376fa7a31146106035780637c3a00fd1461061657806381ebdd791461061f5780638ac08bf11461063f57600080fd5b80633a2b6939116102fa5780635c40f6f41161028d5780636666e49c1161025c5780636666e49c146105d35780636f3b993f146105db57806370740aab146105e857806370a08231146105f057600080fd5b80635c40f6f4146105875780635ccef3e71461059a5780635f84f302146105ad57806363783444146105c057600080fd5b8063428630f6116102c9578063428630f61461056657806344fd9caa1461056e57806358c3b8701461057757806359d936171461057f57600080fd5b80633a2b6939146105255780633b18b9841461052d57806340615cf81461054057806340eba90e1461055357600080fd5b806323b872dd116103725780632e0ad004116103415780632e0ad004146104fa57806331cef44714610502578063329dbddc1461051557806336f7ab5e1461051d57600080fd5b806323b872dd146104a157806325287d42146104b457806325d60861146104c757806327e235e3146104da57600080fd5b8063153a1f3e116103ae578063153a1f3e1461045757806318160ddd1461046a5780631935a88014610481578063200d2ed21461049457600080fd5b8063034f6b21146103e057806306eaa0b71461040257806306fdde03146104175780630af7eb0f1461042c575b600080fd5b6018546103ed9060ff1681565b60405190151581526020015b60405180910390f35b610415610410366004612cc8565b61088e565b005b61041f610b58565b6040516103f99190612d55565b60095461043f906001600160a01b031681565b6040516001600160a01b0390911681526020016103f9565b6103ed610465366004612e17565b610be6565b61047360035481565b6040519081526020016103f9565b61041561048f366004612ede565b610de9565b6007546103ed9060ff1681565b6103ed6104af366004612efb565b610e7b565b60045461043f906001600160a01b031681565b6104156104d5366004612f37565b611062565b6104736104e8366004612f74565b60086020526000908152604090205481565b61041f6110bc565b610415610510366004612cc8565b6110c9565b61041f61122d565b61041f61123a565b61041f611247565b61041561053b366004612f8f565b611254565b61041561054e366004612fa8565b6112d3565b610415610561366004612fd2565b611371565b61041f61163c565b610473600f5481565b61041f611649565b61041f611656565b610415610595366004612ede565b611663565b6104156105a8366004612cc8565b6116f2565b6104156105bb366004612f8f565b611a74565b6104156105ce366004612f37565b611ac3565b61041f611b19565b600b546103ed9060ff1681565b61041f611b26565b6104736105fe366004612f74565b611b33565b610415610611366004612f74565b611b4e565b61047360105481565b61047361062d366004612f74565b600d6020526000908152604090205481565b61041561064d366004612f37565b611bba565b60005461043f906001600160a01b031681565b610415610673366004612f37565b611c10565b610415610686366004612f37565b611c66565b6009546103ed90600160a01b900460ff1681565b6106b26106ad366004612f74565b611cbc565b6040516103f993929190613029565b61041f611d67565b6104156106d7366004612f37565b611d74565b6104156106ea366004612ede565b611dca565b6103ed6106fd366004612fa8565b611e32565b610415610710366004612f37565b611ef3565b6009546103ed90600160a81b900460ff1681565b610415610737366004612f37565b611f49565b61047361074a366004613051565b611f9f565b61041561075d366004613084565b611fca565b610415610770366004612fd2565b6120b3565b610415610783366004612ede565b612190565b6104156107963660046130ec565b612223565b610415612372565b6104736107b1366004613051565b600e60209081526000928352604080842090915290825290205481565b6104156107dc366004612efb565b6123f4565b61041f6125c2565b61047360135481565b610415610800366004612f8f565b6125cf565b610415610813366004612f74565b61264e565b61041f612738565b61041561082e366004612f74565b612745565b61041f6127b1565b61084e610849366004612f8f565b6127be565b604080516001600160a01b03958616815294909316602085015291830152151560608201526080016103f9565b610415610889366004612efb565b612807565b336001600160a01b0316600c83815481106108ab576108ab613165565b60009182526020909120600490910201546001600160a01b0316148015906108de57506000546001600160a01b03163314155b1561092057604080518082018252600681526531323038303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b60405180910390fd5b600c828154811061093357610933613165565b6000918252602082206003600490920201015460ff16151590036109855760408051808201825260068152651899181c181960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b600c828154811061099857610998613165565b90600052602060002090600402016002015460086000600c85815481106109c1576109c1613165565b600091825260208083206004909202909101546001600160a01b03168352820192909252604001812080549091906109fa908490613191565b9091555050600c805483908110610a1357610a13613165565b906000526020600020906004020160020154600d6000600c8581548110610a3c57610a3c613165565b600091825260208083206004909202909101546001600160a01b0316835282019290925260400181208054909190610a759084906131a4565b925050819055506000600c8381548110610a9157610a91613165565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817fa0cc3a4feb26502e6e6cd57b2113d58bdab80c6711d8252356be9a49945bb5f7600c8481548110610aee57610aee613165565b6000918252602090912060049091020154600c80546001600160a01b039092169186908110610b1f57610b1f613165565b6000918252602090912060016004909202010154604051610b4c92916001600160a01b03169086906131b7565b60405180910390a25050565b60018054610b65906131e3565b80601f0160208091040260200160405190810160405280929190818152602001828054610b91906131e3565b8015610bde5780601f10610bb357610100808354040283529160200191610bde565b820191906000526020600020905b815481529060010190602001808311610bc157829003601f168201915b505050505081565b60008151835114610c2557604080518082018252600681526531323035303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6000805b8451811015610c6157838181518110610c4457610c44613165565b602002602001015182610c579190613191565b9150600101610c29565b5080610c6c33611b33565b1015610ca65760408051808201825260068152651899181a981960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b6004546001600160a01b03163314610d075760095460408051808201909152600681526531323035303360d01b602082015290600160a01b900460ff161515600114610d055760405162461bcd60e51b81526004016109179190612d55565b505b606060006001935060005b8651811015610dde57610d3e878281518110610d3057610d30613165565b60200260200101513b151590565b15610d8757610d80878281518110610d5857610d58613165565b6020026020010151878381518110610d7257610d72613165565b60200260200101518561292f565b9150610dc7565b610dc4878281518110610d9c57610d9c613165565b6020026020010151878381518110610db657610db6613165565b602002602001015185612a4a565b91505b811515600003610dd657600094505b600101610d12565b505050505b92915050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610e325760405162461bcd60e51b81526004016109179190612d55565b50600b805460ff19168215159081179091556040519081527fe1455bc53682229469d6919bb96d7b338cbffea93170dfb5f02242c22fb6d07f906020015b60405180910390a150565b6000805460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610ec55760405162461bcd60e51b81526004016109179190612d55565b5081610ed085611b33565b1015610f0a57604080518082018252600681526531323036303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6060833b15610fcf57610f2683610f2087611b33565b90612bf2565b6001600160a01b038616600090815260086020526040902055610f5283610f4c86611b33565b90612c05565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a90610f979033908890879060040161321d565b600060405180830381600087803b158015610fb157600080fd5b505af1158015610fc5573d6000803e3d6000fd5b505050505061101c565b610fdc83610f2087611b33565b6001600160a01b03861660009081526008602052604090205561100283610f4c86611b33565b6001600160a01b0385166000908152600860205260409020555b836001600160a01b0316856001600160a01b03166000805160206133c98339815191528560405161104f91815260200190565b60405180910390a3506001949350505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146110ab5760405162461bcd60e51b81526004016109179190612d55565b5060176110b88282613295565b5050565b601a8054610b65906131e3565b60095460408051808201909152600681526531323130303160d01b602082015290600160a81b900460ff1615156001146111165760405162461bcd60e51b81526004016109179190612d55565b5060095460005460405163d3da927f60e01b81523360048201526001600160a01b03918216602482015291169063d3da927f90604401602060405180830381865afa158015611169573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118d9190613355565b60408051808201909152600681526518991898181960d11b60208201529015156001146111cd5760405162461bcd60e51b81526004016109179190612d55565b50336000908152600a602052604090208281556002016111ed8282613295565b507f3bb9553f633a7ecb4cbc5f9885e750a522496ce06deff1ed9184180ca83940c03383836040516112219392919061321d565b60405180910390a15050565b60118054610b65906131e3565b60058054610b65906131e3565b60198054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461129d5760405162461bcd60e51b81526004016109179190612d55565b5060138190556040518181527fe451abe602c33e612ea61221b5a142f7fe3c044286e42c8340ffe7fa86a5859790602001610e70565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461131c5760405162461bcd60e51b81526004016109179190612d55565b506001600160a01b0382166000818152600a6020908152604091829020600101849055815192835282018390527fa42bd6bdfcb8f9b815c68e4ce0e16dec3f1e7ac53967ee8309653de522a735b19101611221565b600b5460ff16158061138d5750600954600160a01b900460ff16155b8061139f57508161139d33611b33565b105b156113d857604080518082018252600681526531323037303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6000546001600160a01b038481169116146114a75760095460005460405163d3da927f60e01b81526001600160a01b038681166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015611441573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114659190613355565b6040805180820190915260068152651899181b981960d11b60208201529015156001146114a55760405162461bcd60e51b81526004016109179190612d55565b505b33600090815260086020526040812080548492906114c69084906131a4565b9091555050336000908152600d6020526040812080548492906114ea908490613191565b9091555050600c805460408051608081018252338082526001600160a01b038881166020840190815283850189815260016060860181815290880189556000989098529351600487027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7810180549285166001600160a01b031993841617905591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8830180549190941691161790915591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c983015593517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca909101805491151560ff1990921691909117905551909182917fa256c3bc72c26fbfd6285cfde8c7e345b4d0c7ccfc725d103e9f129c5d8a039a9161162e91889088908890613372565b60405180910390a250505050565b60068054610b65906131e3565b60178054610b65906131e3565b601b8054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146116ac5760405162461bcd60e51b81526004016109179190612d55565b506007805460ff191682151590811790915560405160ff9091161515907fe1e5c6f5867805bf3fab73f4df4b80d0e87e9262a7bc22ac7c2cb3fd5896222f90600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461173b5760405162461bcd60e51b81526004016109179190612d55565b50600954600160a01b900460ff16151560000361178657604080518082018252600681526531323039303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b600c828154811061179957611799613165565b6000918252602082206003600490920201015460ff16151590036117eb5760408051808201825260068152651899181c981960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b600c82815481106117fe576117fe613165565b90600052602060002090600402016002015460086000600c858154811061182757611827613165565b60009182526020808320600160049093020191909101546001600160a01b0316835282019290925260400181208054909190611864908490613191565b9091555050600c80548390811061187d5761187d613165565b906000526020600020906004020160020154600d6000600c85815481106118a6576118a6613165565b600091825260208083206004909202909101546001600160a01b03168352820192909252604001812080549091906118df9084906131a4565b925050819055506000600c83815481106118fb576118fb613165565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817f6b50d6e4941ce7ae74ed7f7f51df5fb7680754c2815a094c66bea5ca80a40eb1600c848154811061195857611958613165565b6000918252602090912060049091020154600c80546001600160a01b03909216918690811061198957611989613165565b60009182526020909120600160049092020101546040516119b692916001600160a01b03169086906131b7565b60405180910390a2600c82815481106119d1576119d1613165565b6000918252602090912060016004909202010154600c80546001600160a01b039092169184908110611a0557611a05613165565b6000918252602090912060049091020154600c80546001600160a01b03909216916000805160206133c9833981519152919086908110611a4757611a47613165565b906000526020600020906004020160020154604051611a6891815260200190565b60405180910390a35050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611abd5760405162461bcd60e51b81526004016109179190612d55565b50601055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b0c5760405162461bcd60e51b81526004016109179190612d55565b5060116110b88282613295565b60128054610b65906131e3565b60168054610b65906131e3565b6001600160a01b031660009081526008602052604090205490565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b975760405162461bcd60e51b81526004016109179190612d55565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c035760405162461bcd60e51b81526004016109179190612d55565b5060196110b88282613295565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c595760405162461bcd60e51b81526004016109179190612d55565b5060066110b88282613295565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611caf5760405162461bcd60e51b81526004016109179190612d55565b50601b6110b88282613295565b600a6020526000908152604090208054600182015460028301805492939192611ce4906131e3565b80601f0160208091040260200160405190810160405280929190818152602001828054611d10906131e3565b8015611d5d5780601f10611d3257610100808354040283529160200191611d5d565b820191906000526020600020905b815481529060010190602001808311611d4057829003601f168201915b5050505050905083565b60028054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611dbd5760405162461bcd60e51b81526004016109179190612d55565b50601c6110b88282613295565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611e135760405162461bcd60e51b81526004016109179190612d55565b5060098054911515600160a01b0260ff60a01b19909216919091179055565b600081611e3e33611b33565b10156040518060400160405280600681526020016531323034303160d01b81525090611e7d5760405162461bcd60e51b81526004016109179190612d55565b506009546040805180820190915260068152651899181a181960d11b602082015290600160a01b900460ff161515600114611ecb5760405162461bcd60e51b81526004016109179190612d55565b506060833b15611ee857611ee084848361292f565b915050610de3565b611ee0848483612a4a565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611f3c5760405162461bcd60e51b81526004016109179190612d55565b50601a6110b88282613295565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611f925760405162461bcd60e51b81526004016109179190612d55565b5060056110b88282613295565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205490565b81611fd53386611f9f565b101561200f57604080518082018252600681526518991818981960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b61201d82610f203387611f9f565b336000908152600e602090815260408083206001600160a01b038916845290915290205561204e82610f4c85611b33565b6001600160a01b038085166000908152600860205260409081902092909255905133918616907f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc906120a59087908790879061321d565b60405180910390a350505050565b816120bd33611b33565b10156120f757604080518082018252600681526518991818181960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b61210482610f2033611b33565b33600081815260086020526040902091909155612128908390610f4c908690611f9f565b6001600160a01b0384166000818152600e602090815260408083203380855292529182902093909355519091907ecd77abc69e7bbb6de9df8cbfcca8720df3a30decf9eefd187e72ab2ea2f5139061218390869086906133af565b60405180910390a3505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146121d95760405162461bcd60e51b81526004016109179190612d55565b506009805460ff60a81b1916600160a81b831515908102919091179091556040517fa0c691180b8089c0c84e478ee67d6325768666cd889f13de016771c6f7af1f8890600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461226c5760405162461bcd60e51b81526004016109179190612d55565b50816122788686611f9f565b10156122b257604080518082018252600681526531323132303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6122c082610f208787611f9f565b6001600160a01b038087166000908152600e60209081526040808320938916835292905220556122f382610f4c85611b33565b60086000856001600160a01b03166001600160a01b0316815260200190815260200160002081905550846001600160a01b0316846001600160a01b03167f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc8585856040516123639392919061321d565b60405180910390a35050505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146123bb5760405162461bcd60e51b81526004016109179190612d55565b506018805460ff191660011790556040517f96151d7f1d8c50be29a45c0e3ae9ce2008888d1615b196e8f5ffd8c178e6dde890600090a1565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461243d5760405162461bcd60e51b81526004016109179190612d55565b506001600160a01b038216156124db57806124588385611f9f565b101561249257604080518082018252600681526531323131303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6124a081610f208486611f9f565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546124d39082612bf2565b600355612566565b6001600160a01b03831660009081526008602052604090205481111561252f57604080518082018252600681526518991898981960d11b6020820152905162461bcd60e51b81526109179190600401612d55565b61253c81610f2085611b33565b6001600160a01b0384166000908152600860205260409020556003546125629082612bf2565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167fee02732fab40ece8284c756220846dff4b8d32058b86b35b4f0459bf172fcef0846040516125b591815260200190565b60405180910390a4505050565b601c8054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146126185760405162461bcd60e51b81526004016109179190612d55565b50600f8190556040518181527f82b19c9a00131a87eeff1903fcb9e4f2a3ba76dd86098586eea921a533e218dc90602001610e70565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146126975760405162461bcd60e51b81526004016109179190612d55565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b0382166126dc5760405162461bcd60e51b81526004016109179190612d55565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60158054610b65906131e3565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461278e5760405162461bcd60e51b81526004016109179190612d55565b50600980546001600160a01b0319166001600160a01b0392909216919091179055565b60148054610b65906131e3565b600c81815481106127ce57600080fd5b600091825260209091206004909102018054600182015460028301546003909301546001600160a01b0392831694509116919060ff1684565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146128505760405162461bcd60e51b81526004016109179190612d55565b506001600160a01b038216156128a95761286e81610f4c8486611f9f565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546128a19082612c05565b6003556128e0565b6128b681610f4c85611b33565b6001600160a01b0384166000908152600860205260409020556003546128dc9082612c05565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167f65adeb76912378393e600cb6f64f3310842a42e1eae86273dc775d0c47a0f2dc846040516125b591815260200190565b60045460408051808201909152600681526531323033303160d01b60208201526000916001600160a01b0386811691161461297d5760405162461bcd60e51b81526004016109179190612d55565b5061298b83610f2033611b33565b336000908152600860205260409020556129a883610f4c86611b33565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a906129ed9033908890889060040161321d565b600060405180830381600087803b158015612a0757600080fd5b505af1158015612a1b573d6000803e3d6000fd5b50506040518681526001600160a01b03881692503391506000805160206133c98339815191529060200161104f565b6004546000906001600160a01b03163314801590612a6f5750600b5460ff1615156001145b15612aa857604080518082018252600681526531323032303160d01b6020820152905162461bcd60e51b81526109179190600401612d55565b6000546001600160a01b03858116911614612b775760095460005460405163d3da927f60e01b81526001600160a01b038781166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015612b11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b359190613355565b60408051808201909152600681526518991819181960d11b6020820152901515600114612b755760405162461bcd60e51b81526004016109179190612d55565b505b612b8483610f2033611b33565b33600090815260086020526040902055612ba183610f4c86611b33565b6001600160a01b0385166000818152600860205260409081902092909255905133906000805160206133c983398151915290612be09087815260200190565b60405180910390a35060019392505050565b6000612bfe82846131a4565b9392505050565b6000612bfe8284613191565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612c5057612c50612c11565b604052919050565b600082601f830112612c6957600080fd5b813567ffffffffffffffff811115612c8357612c83612c11565b612c96601f8201601f1916602001612c27565b818152846020838601011115612cab57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215612cdb57600080fd5b82359150602083013567ffffffffffffffff811115612cf957600080fd5b612d0585828601612c58565b9150509250929050565b6000815180845260005b81811015612d3557602081850181015186830182015201612d19565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612bfe6020830184612d0f565b600067ffffffffffffffff821115612d8257612d82612c11565b5060051b60200190565b80356001600160a01b0381168114612da357600080fd5b919050565b600082601f830112612db957600080fd5b81356020612dce612dc983612d68565b612c27565b8083825260208201915060208460051b870101935086841115612df057600080fd5b602086015b84811015612e0c5780358352918301918301612df5565b509695505050505050565b60008060408385031215612e2a57600080fd5b823567ffffffffffffffff80821115612e4257600080fd5b818501915085601f830112612e5657600080fd5b81356020612e66612dc983612d68565b82815260059290921b84018101918181019089841115612e8557600080fd5b948201945b83861015612eaa57612e9b86612d8c565b82529482019490820190612e8a565b96505086013592505080821115612ec057600080fd5b50612d0585828601612da8565b8015158114612edb57600080fd5b50565b600060208284031215612ef057600080fd5b8135612bfe81612ecd565b600080600060608486031215612f1057600080fd5b612f1984612d8c565b9250612f2760208501612d8c565b9150604084013590509250925092565b600060208284031215612f4957600080fd5b813567ffffffffffffffff811115612f6057600080fd5b612f6c84828501612c58565b949350505050565b600060208284031215612f8657600080fd5b612bfe82612d8c565b600060208284031215612fa157600080fd5b5035919050565b60008060408385031215612fbb57600080fd5b612fc483612d8c565b946020939093013593505050565b600080600060608486031215612fe757600080fd5b612ff084612d8c565b925060208401359150604084013567ffffffffffffffff81111561301357600080fd5b61301f86828701612c58565b9150509250925092565b8381528260208201526060604082015260006130486060830184612d0f565b95945050505050565b6000806040838503121561306457600080fd5b61306d83612d8c565b915061307b60208401612d8c565b90509250929050565b6000806000806080858703121561309a57600080fd5b6130a385612d8c565b93506130b160208601612d8c565b925060408501359150606085013567ffffffffffffffff8111156130d457600080fd5b6130e087828801612c58565b91505092959194509250565b600080600080600060a0868803121561310457600080fd5b61310d86612d8c565b945061311b60208701612d8c565b935061312960408701612d8c565b925060608601359150608086013567ffffffffffffffff81111561314c57600080fd5b61315888828901612c58565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610de357610de361317b565b81810381811115610de357610de361317b565b6001600160a01b0384811682528316602082015260606040820181905260009061304890830184612d0f565b600181811c908216806131f757607f821691505b60208210810361321757634e487b7160e01b600052602260045260246000fd5b50919050565b60018060a01b03841681528260208201526060604082015260006130486060830184612d0f565b601f821115613290576000816000526020600020601f850160051c8101602086101561326d5750805b601f850160051c820191505b8181101561328c57828155600101613279565b5050505b505050565b815167ffffffffffffffff8111156132af576132af612c11565b6132c3816132bd84546131e3565b84613244565b602080601f8311600181146132f857600084156132e05750858301515b600019600386901b1c1916600185901b17855561328c565b600085815260208120601f198616915b8281101561332757888601518255948401946001909101908401613308565b50858210156133455787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561336757600080fd5b8151612bfe81612ecd565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906133a590830184612d0f565b9695505050505050565b828152604060208201526000612f6c6040830184612d0f56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122088f29532258abd280c78b38a51851d03be753cbc68978a3c5f7fbc262f37bfa964736f6c63430008170033" + "bytecode": "60806040523480156200001157600080fd5b5060405162003a1f38038062003a1f8339810160408190526200003491620001f5565b600080546001600160a01b0319908116339182161717905560016200005a8c8262000431565b506002620000698b8262000431565b506003899055600f889055601962000082888262000431565b50601262000091878262000431565b506013859055601b620000a5858262000431565b506014620000b4848262000431565b506015620000c3838262000431565b506016620000d2828262000431565b5050600354600080546001600160a01b031681526008602052604090205550506018805460ff1990811690915560078054909116600117905550506009805460ff60a01b1916600160a01b17905550620004fd945050505050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200015557600080fd5b81516001600160401b03808211156200017257620001726200012d565b604051601f8301601f19908116603f011681019082821181831017156200019d576200019d6200012d565b8160405283815260209250866020858801011115620001bb57600080fd5b600091505b83821015620001df5785820183015181830184015290820190620001c0565b6000602085830101528094505050505092915050565b60008060008060008060008060008060006101608c8e0312156200021857600080fd5b8b516001600160401b038111156200022f57600080fd5b6200023d8e828f0162000143565b60208e0151909c5090506001600160401b038111156200025c57600080fd5b6200026a8e828f0162000143565b9a505060408c0151985060608c0151975060808c015160018060401b038111156200029457600080fd5b620002a28e828f0162000143565b60a08e015190985090506001600160401b03811115620002c157600080fd5b620002cf8e828f0162000143565b60c08e015160e08f0151919850965090506001600160401b03811115620002f557600080fd5b620003038e828f0162000143565b6101008e015190955090506001600160401b038111156200032357600080fd5b620003318e828f0162000143565b6101208e015190945090506001600160401b038111156200035157600080fd5b6200035f8e828f0162000143565b6101408e015190935090506001600160401b038111156200037f57600080fd5b6200038d8e828f0162000143565b9150509295989b509295989b9093969950565b600181811c90821680620003b557607f821691505b602082108103620003d657634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200042c576000816000526020600020601f850160051c81016020861015620004075750805b601f850160051c820191505b81811015620004285782815560010162000413565b5050505b505050565b81516001600160401b038111156200044d576200044d6200012d565b62000465816200045e8454620003a0565b84620003dc565b602080601f8311600181146200049d5760008415620004845750858301515b600019600386901b1c1916600185901b17855562000428565b600085815260208120601f198616915b82811015620004ce57888601518255948401946001909101908401620004ad565b5085821015620004ed5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b613512806200050d6000396000f3fe608060405234801561001057600080fd5b50600436106103f15760003560e01c80637c3a00fd11610215578063c267ce5f11610125578063ef8032ef116100b8578063fc590d0911610087578063fc590d0914610849578063fd0e856914610851578063fd14a22214610865578063fef1b9c014610878578063ff12dcea146108b857600080fd5b8063ef8032ef14610808578063f2fde38b1461081b578063f35eeaef1461082e578063f7502a7c1461083657600080fd5b8063db20266f116100f4578063db20266f146107b9578063e7464db1146107e4578063ed62c2a0146107f7578063ef4474cd146107ff57600080fd5b8063c267ce5f14610778578063c7109ec91461078b578063ca5cbbb51461079e578063d5d9d0c7146107b157600080fd5b806395d89b41116101a8578063ab62f92e11610177578063ab62f92e14610718578063b2a90a601461072b578063b967a52e1461073f578063b9b3e06a14610752578063bfe30fb01461076557600080fd5b806395d89b41146106d757806397e4c513146106df5780639cd23707146106f2578063a9059cbb1461070557600080fd5b80638ed8a9bc116101e45780638ed8a9bc1461067b578063919fe52d1461068e57806392ff0d31146106a15780639301a78b146106b557600080fd5b80637c3a00fd1461062c57806381ebdd79146106355780638ac08bf1146106555780638da5cb5b1461066857600080fd5b80633a2b6939116103105780635c40f6f4116102a35780636666e49c116102725780636666e49c146105e95780636f3b993f146105f157806370740aab146105fe57806370a082311461060657806376fa7a311461061957600080fd5b80635c40f6f41461059d5780635ccef3e7146105b05780635f84f302146105c357806363783444146105d657600080fd5b8063428630f6116102df578063428630f61461057c57806344fd9caa1461058457806358c3b8701461058d57806359d936171461059557600080fd5b80633a2b69391461053b5780633b18b9841461054357806340615cf81461055657806340eba90e1461056957600080fd5b806323b872dd116103885780632e0ad004116103575780632e0ad0041461051057806331cef44714610518578063329dbddc1461052b57806336f7ab5e1461053357600080fd5b806323b872dd146104b757806325287d42146104ca57806325d60861146104dd57806327e235e3146104f057600080fd5b8063153a1f3e116103c4578063153a1f3e1461046d57806318160ddd146104805780631935a88014610497578063200d2ed2146104aa57600080fd5b8063034f6b21146103f657806306eaa0b71461041857806306fdde031461042d5780630af7eb0f14610442575b600080fd5b6018546104039060ff1681565b60405190151581526020015b60405180910390f35b61042b610426366004612dbc565b6108cb565b005b610435610b95565b60405161040f9190612e49565b600954610455906001600160a01b031681565b6040516001600160a01b03909116815260200161040f565b61040361047b366004612f0b565b610c23565b61048960035481565b60405190815260200161040f565b61042b6104a5366004612fd2565b610e26565b6007546104039060ff1681565b6104036104c5366004612fef565b610eb8565b600454610455906001600160a01b031681565b61042b6104eb36600461302b565b61109f565b6104896104fe366004613068565b60086020526000908152604090205481565b6104356110f9565b61042b610526366004612dbc565b611106565b610435611281565b61043561128e565b61043561129b565b61042b610551366004613083565b6112a8565b61042b61056436600461309c565b611327565b61042b6105773660046130c6565b6113c5565b6104356116ac565b610489600f5481565b6104356116b9565b6104356116c6565b61042b6105ab366004612fd2565b6116d3565b61042b6105be366004612dbc565b611762565b61042b6105d1366004613083565b611ae4565b61042b6105e436600461302b565b611b33565b610435611b89565b600b546104039060ff1681565b610435611b96565b610489610614366004613068565b611ba3565b61042b610627366004613068565b611bbe565b61048960105481565b610489610643366004613068565b600d6020526000908152604090205481565b61042b61066336600461302b565b611c2a565b600054610455906001600160a01b031681565b61042b61068936600461302b565b611c80565b61042b61069c36600461302b565b611cd6565b60095461040390600160a81b900460ff1681565b6106c86106c3366004613068565b611d2c565b60405161040f9392919061311d565b610435611dd7565b61042b6106ed36600461302b565b611de4565b61042b610700366004612fd2565b611e3a565b61040361071336600461309c565b611ea2565b61042b61072636600461302b565b611f63565b60095461040390600160b01b900460ff1681565b61042b61074d36600461302b565b611fb9565b610489610760366004613145565b61200f565b61042b610773366004613178565b61203a565b61042b6107863660046130c6565b612123565b61042b610799366004612fd2565b612200565b61042b6107ac3660046131e0565b612293565b61042b6123e2565b6104896107c7366004613145565b600e60209081526000928352604080842090915290825290205481565b61042b6107f2366004612fef565b612464565b610435612632565b61048960135481565b61042b610816366004613083565b61263f565b61042b610829366004613068565b6126be565b6104356127a8565b61042b610844366004613068565b6127b5565b610435612821565b60095461040390600160a01b900460ff1681565b61042b610873366004612fd2565b61282e565b61088b610886366004613083565b612896565b604080516001600160a01b039586168152949093166020850152918301521515606082015260800161040f565b61042b6108c6366004612fef565b6128df565b336001600160a01b0316600c83815481106108e8576108e8613259565b60009182526020909120600490910201546001600160a01b03161480159061091b57506000546001600160a01b03163314155b1561095d57604080518082018252600681526531323038303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b60405180910390fd5b600c828154811061097057610970613259565b6000918252602082206003600490920201015460ff16151590036109c25760408051808201825260068152651899181c181960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b600c82815481106109d5576109d5613259565b90600052602060002090600402016002015460086000600c85815481106109fe576109fe613259565b600091825260208083206004909202909101546001600160a01b0316835282019290925260400181208054909190610a37908490613285565b9091555050600c805483908110610a5057610a50613259565b906000526020600020906004020160020154600d6000600c8581548110610a7957610a79613259565b600091825260208083206004909202909101546001600160a01b0316835282019290925260400181208054909190610ab2908490613298565b925050819055506000600c8381548110610ace57610ace613259565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817fa0cc3a4feb26502e6e6cd57b2113d58bdab80c6711d8252356be9a49945bb5f7600c8481548110610b2b57610b2b613259565b6000918252602090912060049091020154600c80546001600160a01b039092169186908110610b5c57610b5c613259565b6000918252602090912060016004909202010154604051610b8992916001600160a01b03169086906132ab565b60405180910390a25050565b60018054610ba2906132d7565b80601f0160208091040260200160405190810160405280929190818152602001828054610bce906132d7565b8015610c1b5780601f10610bf057610100808354040283529160200191610c1b565b820191906000526020600020905b815481529060010190602001808311610bfe57829003601f168201915b505050505081565b60008151835114610c6257604080518082018252600681526531323035303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b6000805b8451811015610c9e57838181518110610c8157610c81613259565b602002602001015182610c949190613285565b9150600101610c66565b5080610ca933611ba3565b1015610ce35760408051808201825260068152651899181a981960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b6004546001600160a01b03163314610d445760095460408051808201909152600681526531323035303360d01b602082015290600160a81b900460ff161515600114610d425760405162461bcd60e51b81526004016109549190612e49565b505b606060006001935060005b8651811015610e1b57610d7b878281518110610d6d57610d6d613259565b60200260200101513b151590565b15610dc457610dbd878281518110610d9557610d95613259565b6020026020010151878381518110610daf57610daf613259565b602002602001015185612a07565b9150610e04565b610e01878281518110610dd957610dd9613259565b6020026020010151878381518110610df357610df3613259565b602002602001015185612b22565b91505b811515600003610e1357600094505b600101610d4f565b505050505b92915050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610e6f5760405162461bcd60e51b81526004016109549190612e49565b50600b805460ff19168215159081179091556040519081527fe1455bc53682229469d6919bb96d7b338cbffea93170dfb5f02242c22fb6d07f906020015b60405180910390a150565b6000805460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610f025760405162461bcd60e51b81526004016109549190612e49565b5081610f0d85611ba3565b1015610f4757604080518082018252600681526531323036303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b6060833b1561100c57610f6383610f5d87611ba3565b90612ce6565b6001600160a01b038616600090815260086020526040902055610f8f83610f8986611ba3565b90612cf9565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a90610fd490339088908790600401613311565b600060405180830381600087803b158015610fee57600080fd5b505af1158015611002573d6000803e3d6000fd5b5050505050611059565b61101983610f5d87611ba3565b6001600160a01b03861660009081526008602052604090205561103f83610f8986611ba3565b6001600160a01b0385166000908152600860205260409020555b836001600160a01b0316856001600160a01b03166000805160206134bd8339815191528560405161108c91815260200190565b60405180910390a3506001949350505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146110e85760405162461bcd60e51b81526004016109549190612e49565b5060176110f58282613389565b5050565b601a8054610ba2906132d7565b60095460408051808201909152600681526531323130303160d01b602082015290600160b01b900460ff1615156001146111535760405162461bcd60e51b81526004016109549190612e49565b50600954600160a01b900460ff1615156001036112225760095460005460405163d3da927f60e01b81523360048201526001600160a01b03918216602482015291169063d3da927f90604401602060405180830381865afa1580156111bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e09190613449565b60408051808201909152600681526518991898181960d11b60208201529015156001146112205760405162461bcd60e51b81526004016109549190612e49565b505b336000908152600a602052604090208281556002016112418282613389565b507f3bb9553f633a7ecb4cbc5f9885e750a522496ce06deff1ed9184180ca83940c033838360405161127593929190613311565b60405180910390a15050565b60118054610ba2906132d7565b60058054610ba2906132d7565b60198054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146112f15760405162461bcd60e51b81526004016109549190612e49565b5060138190556040518181527fe451abe602c33e612ea61221b5a142f7fe3c044286e42c8340ffe7fa86a5859790602001610ead565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146113705760405162461bcd60e51b81526004016109549190612e49565b506001600160a01b0382166000818152600a6020908152604091829020600101849055815192835282018390527fa42bd6bdfcb8f9b815c68e4ce0e16dec3f1e7ac53967ee8309653de522a735b19101611275565b600b5460ff1615806113e15750600954600160a81b900460ff16155b806113f35750816113f133611ba3565b105b1561142c57604080518082018252600681526531323037303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b6000546001600160a01b038481169116148015906114585750600954600160a01b900460ff1615156001145b156115175760095460005460405163d3da927f60e01b81526001600160a01b038681166004830152918216602482015291169063d3da927f90604401602060405180830381865afa1580156114b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d59190613449565b6040805180820190915260068152651899181b981960d11b60208201529015156001146115155760405162461bcd60e51b81526004016109549190612e49565b505b3360009081526008602052604081208054849290611536908490613298565b9091555050336000908152600d60205260408120805484929061155a908490613285565b9091555050600c805460408051608081018252338082526001600160a01b038881166020840190815283850189815260016060860181815290880189556000989098529351600487027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7810180549285166001600160a01b031993841617905591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8830180549190941691161790915591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c983015593517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca909101805491151560ff1990921691909117905551909182917fa256c3bc72c26fbfd6285cfde8c7e345b4d0c7ccfc725d103e9f129c5d8a039a9161169e91889088908890613466565b60405180910390a250505050565b60068054610ba2906132d7565b60178054610ba2906132d7565b601b8054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461171c5760405162461bcd60e51b81526004016109549190612e49565b506007805460ff191682151590811790915560405160ff9091161515907fe1e5c6f5867805bf3fab73f4df4b80d0e87e9262a7bc22ac7c2cb3fd5896222f90600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146117ab5760405162461bcd60e51b81526004016109549190612e49565b50600954600160a81b900460ff1615156000036117f657604080518082018252600681526531323039303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b600c828154811061180957611809613259565b6000918252602082206003600490920201015460ff161515900361185b5760408051808201825260068152651899181c981960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b600c828154811061186e5761186e613259565b90600052602060002090600402016002015460086000600c858154811061189757611897613259565b60009182526020808320600160049093020191909101546001600160a01b03168352820192909252604001812080549091906118d4908490613285565b9091555050600c8054839081106118ed576118ed613259565b906000526020600020906004020160020154600d6000600c858154811061191657611916613259565b600091825260208083206004909202909101546001600160a01b031683528201929092526040018120805490919061194f908490613298565b925050819055506000600c838154811061196b5761196b613259565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817f6b50d6e4941ce7ae74ed7f7f51df5fb7680754c2815a094c66bea5ca80a40eb1600c84815481106119c8576119c8613259565b6000918252602090912060049091020154600c80546001600160a01b0390921691869081106119f9576119f9613259565b6000918252602090912060016004909202010154604051611a2692916001600160a01b03169086906132ab565b60405180910390a2600c8281548110611a4157611a41613259565b6000918252602090912060016004909202010154600c80546001600160a01b039092169184908110611a7557611a75613259565b6000918252602090912060049091020154600c80546001600160a01b03909216916000805160206134bd833981519152919086908110611ab757611ab7613259565b906000526020600020906004020160020154604051611ad891815260200190565b60405180910390a35050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b2d5760405162461bcd60e51b81526004016109549190612e49565b50601055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b7c5760405162461bcd60e51b81526004016109549190612e49565b5060116110f58282613389565b60128054610ba2906132d7565b60168054610ba2906132d7565b6001600160a01b031660009081526008602052604090205490565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c075760405162461bcd60e51b81526004016109549190612e49565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c735760405162461bcd60e51b81526004016109549190612e49565b5060196110f58282613389565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611cc95760405162461bcd60e51b81526004016109549190612e49565b5060066110f58282613389565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611d1f5760405162461bcd60e51b81526004016109549190612e49565b50601b6110f58282613389565b600a6020526000908152604090208054600182015460028301805492939192611d54906132d7565b80601f0160208091040260200160405190810160405280929190818152602001828054611d80906132d7565b8015611dcd5780601f10611da257610100808354040283529160200191611dcd565b820191906000526020600020905b815481529060010190602001808311611db057829003601f168201915b5050505050905083565b60028054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611e2d5760405162461bcd60e51b81526004016109549190612e49565b50601c6110f58282613389565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611e835760405162461bcd60e51b81526004016109549190612e49565b5060098054911515600160a81b0260ff60a81b19909216919091179055565b600081611eae33611ba3565b10156040518060400160405280600681526020016531323034303160d01b81525090611eed5760405162461bcd60e51b81526004016109549190612e49565b506009546040805180820190915260068152651899181a181960d11b602082015290600160a81b900460ff161515600114611f3b5760405162461bcd60e51b81526004016109549190612e49565b506060833b15611f5857611f50848483612a07565b915050610e20565b611f50848483612b22565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611fac5760405162461bcd60e51b81526004016109549190612e49565b50601a6110f58282613389565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146120025760405162461bcd60e51b81526004016109549190612e49565b5060056110f58282613389565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205490565b81612045338661200f565b101561207f57604080518082018252600681526518991818981960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b61208d82610f5d338761200f565b336000908152600e602090815260408083206001600160a01b03891684529091529020556120be82610f8985611ba3565b6001600160a01b038085166000908152600860205260409081902092909255905133918616907f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc9061211590879087908790613311565b60405180910390a350505050565b8161212d33611ba3565b101561216757604080518082018252600681526518991818181960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b61217482610f5d33611ba3565b33600081815260086020526040902091909155612198908390610f8990869061200f565b6001600160a01b0384166000818152600e602090815260408083203380855292529182902093909355519091907ecd77abc69e7bbb6de9df8cbfcca8720df3a30decf9eefd187e72ab2ea2f513906121f390869086906134a3565b60405180910390a3505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146122495760405162461bcd60e51b81526004016109549190612e49565b506009805460ff60b01b1916600160b01b831515908102919091179091556040517fa0c691180b8089c0c84e478ee67d6325768666cd889f13de016771c6f7af1f8890600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146122dc5760405162461bcd60e51b81526004016109549190612e49565b50816122e8868661200f565b101561232257604080518082018252600681526531323132303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b61233082610f5d878761200f565b6001600160a01b038087166000908152600e602090815260408083209389168352929052205561236382610f8985611ba3565b60086000856001600160a01b03166001600160a01b0316815260200190815260200160002081905550846001600160a01b0316846001600160a01b03167f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc8585856040516123d393929190613311565b60405180910390a35050505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461242b5760405162461bcd60e51b81526004016109549190612e49565b506018805460ff191660011790556040517f96151d7f1d8c50be29a45c0e3ae9ce2008888d1615b196e8f5ffd8c178e6dde890600090a1565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146124ad5760405162461bcd60e51b81526004016109549190612e49565b506001600160a01b0382161561254b57806124c8838561200f565b101561250257604080518082018252600681526531323131303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b61251081610f5d848661200f565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546125439082612ce6565b6003556125d6565b6001600160a01b03831660009081526008602052604090205481111561259f57604080518082018252600681526518991898981960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b6125ac81610f5d85611ba3565b6001600160a01b0384166000908152600860205260409020556003546125d29082612ce6565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167fee02732fab40ece8284c756220846dff4b8d32058b86b35b4f0459bf172fcef08460405161262591815260200190565b60405180910390a4505050565b601c8054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146126885760405162461bcd60e51b81526004016109549190612e49565b50600f8190556040518181527f82b19c9a00131a87eeff1903fcb9e4f2a3ba76dd86098586eea921a533e218dc90602001610ead565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146127075760405162461bcd60e51b81526004016109549190612e49565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b03821661274c5760405162461bcd60e51b81526004016109549190612e49565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60158054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146127fe5760405162461bcd60e51b81526004016109549190612e49565b50600980546001600160a01b0319166001600160a01b0392909216919091179055565b60148054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146128775760405162461bcd60e51b81526004016109549190612e49565b5060098054911515600160a01b0260ff60a01b19909216919091179055565b600c81815481106128a657600080fd5b600091825260209091206004909102018054600182015460028301546003909301546001600160a01b0392831694509116919060ff1684565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146129285760405162461bcd60e51b81526004016109549190612e49565b506001600160a01b038216156129815761294681610f89848661200f565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546129799082612cf9565b6003556129b8565b61298e81610f8985611ba3565b6001600160a01b0384166000908152600860205260409020556003546129b49082612cf9565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167f65adeb76912378393e600cb6f64f3310842a42e1eae86273dc775d0c47a0f2dc8460405161262591815260200190565b60045460408051808201909152600681526531323033303160d01b60208201526000916001600160a01b03868116911614612a555760405162461bcd60e51b81526004016109549190612e49565b50612a6383610f5d33611ba3565b33600090815260086020526040902055612a8083610f8986611ba3565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a90612ac590339088908890600401613311565b600060405180830381600087803b158015612adf57600080fd5b505af1158015612af3573d6000803e3d6000fd5b50506040518681526001600160a01b03881692503391506000805160206134bd8339815191529060200161108c565b6004546000906001600160a01b03163314801590612b475750600b5460ff1615156001145b15612b8057604080518082018252600681526531323032303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b6000546001600160a01b03858116911614801590612bac5750600954600160a01b900460ff1615156001145b15612c6b5760095460005460405163d3da927f60e01b81526001600160a01b038781166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015612c05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c299190613449565b60408051808201909152600681526518991819181960d11b6020820152901515600114612c695760405162461bcd60e51b81526004016109549190612e49565b505b612c7883610f5d33611ba3565b33600090815260086020526040902055612c9583610f8986611ba3565b6001600160a01b0385166000818152600860205260409081902092909255905133906000805160206134bd83398151915290612cd49087815260200190565b60405180910390a35060019392505050565b6000612cf28284613298565b9392505050565b6000612cf28284613285565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612d4457612d44612d05565b604052919050565b600082601f830112612d5d57600080fd5b813567ffffffffffffffff811115612d7757612d77612d05565b612d8a601f8201601f1916602001612d1b565b818152846020838601011115612d9f57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215612dcf57600080fd5b82359150602083013567ffffffffffffffff811115612ded57600080fd5b612df985828601612d4c565b9150509250929050565b6000815180845260005b81811015612e2957602081850181015186830182015201612e0d565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612cf26020830184612e03565b600067ffffffffffffffff821115612e7657612e76612d05565b5060051b60200190565b80356001600160a01b0381168114612e9757600080fd5b919050565b600082601f830112612ead57600080fd5b81356020612ec2612ebd83612e5c565b612d1b565b8083825260208201915060208460051b870101935086841115612ee457600080fd5b602086015b84811015612f005780358352918301918301612ee9565b509695505050505050565b60008060408385031215612f1e57600080fd5b823567ffffffffffffffff80821115612f3657600080fd5b818501915085601f830112612f4a57600080fd5b81356020612f5a612ebd83612e5c565b82815260059290921b84018101918181019089841115612f7957600080fd5b948201945b83861015612f9e57612f8f86612e80565b82529482019490820190612f7e565b96505086013592505080821115612fb457600080fd5b50612df985828601612e9c565b8015158114612fcf57600080fd5b50565b600060208284031215612fe457600080fd5b8135612cf281612fc1565b60008060006060848603121561300457600080fd5b61300d84612e80565b925061301b60208501612e80565b9150604084013590509250925092565b60006020828403121561303d57600080fd5b813567ffffffffffffffff81111561305457600080fd5b61306084828501612d4c565b949350505050565b60006020828403121561307a57600080fd5b612cf282612e80565b60006020828403121561309557600080fd5b5035919050565b600080604083850312156130af57600080fd5b6130b883612e80565b946020939093013593505050565b6000806000606084860312156130db57600080fd5b6130e484612e80565b925060208401359150604084013567ffffffffffffffff81111561310757600080fd5b61311386828701612d4c565b9150509250925092565b83815282602082015260606040820152600061313c6060830184612e03565b95945050505050565b6000806040838503121561315857600080fd5b61316183612e80565b915061316f60208401612e80565b90509250929050565b6000806000806080858703121561318e57600080fd5b61319785612e80565b93506131a560208601612e80565b925060408501359150606085013567ffffffffffffffff8111156131c857600080fd5b6131d487828801612d4c565b91505092959194509250565b600080600080600060a086880312156131f857600080fd5b61320186612e80565b945061320f60208701612e80565b935061321d60408701612e80565b925060608601359150608086013567ffffffffffffffff81111561324057600080fd5b61324c88828901612d4c565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610e2057610e2061326f565b81810381811115610e2057610e2061326f565b6001600160a01b0384811682528316602082015260606040820181905260009061313c90830184612e03565b600181811c908216806132eb57607f821691505b60208210810361330b57634e487b7160e01b600052602260045260246000fd5b50919050565b60018060a01b038416815282602082015260606040820152600061313c6060830184612e03565b601f821115613384576000816000526020600020601f850160051c810160208610156133615750805b601f850160051c820191505b818110156133805782815560010161336d565b5050505b505050565b815167ffffffffffffffff8111156133a3576133a3612d05565b6133b7816133b184546132d7565b84613338565b602080601f8311600181146133ec57600084156133d45750858301515b600019600386901b1c1916600185901b178555613380565b600085815260208120601f198616915b8281101561341b578886015182559484019460019091019084016133fc565b50858210156134395787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561345b57600080fd5b8151612cf281612fc1565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061349990830184612e03565b9695505050505050565b8281526040602082015260006130606040830184612e0356feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122058ccc1e9b73794228a24d20df37b2c765313479f9d25721169fb5f5a2203353764736f6c63430008170033", + "deployedBytecode": "608060405234801561001057600080fd5b50600436106103f15760003560e01c80637c3a00fd11610215578063c267ce5f11610125578063ef8032ef116100b8578063fc590d0911610087578063fc590d0914610849578063fd0e856914610851578063fd14a22214610865578063fef1b9c014610878578063ff12dcea146108b857600080fd5b8063ef8032ef14610808578063f2fde38b1461081b578063f35eeaef1461082e578063f7502a7c1461083657600080fd5b8063db20266f116100f4578063db20266f146107b9578063e7464db1146107e4578063ed62c2a0146107f7578063ef4474cd146107ff57600080fd5b8063c267ce5f14610778578063c7109ec91461078b578063ca5cbbb51461079e578063d5d9d0c7146107b157600080fd5b806395d89b41116101a8578063ab62f92e11610177578063ab62f92e14610718578063b2a90a601461072b578063b967a52e1461073f578063b9b3e06a14610752578063bfe30fb01461076557600080fd5b806395d89b41146106d757806397e4c513146106df5780639cd23707146106f2578063a9059cbb1461070557600080fd5b80638ed8a9bc116101e45780638ed8a9bc1461067b578063919fe52d1461068e57806392ff0d31146106a15780639301a78b146106b557600080fd5b80637c3a00fd1461062c57806381ebdd79146106355780638ac08bf1146106555780638da5cb5b1461066857600080fd5b80633a2b6939116103105780635c40f6f4116102a35780636666e49c116102725780636666e49c146105e95780636f3b993f146105f157806370740aab146105fe57806370a082311461060657806376fa7a311461061957600080fd5b80635c40f6f41461059d5780635ccef3e7146105b05780635f84f302146105c357806363783444146105d657600080fd5b8063428630f6116102df578063428630f61461057c57806344fd9caa1461058457806358c3b8701461058d57806359d936171461059557600080fd5b80633a2b69391461053b5780633b18b9841461054357806340615cf81461055657806340eba90e1461056957600080fd5b806323b872dd116103885780632e0ad004116103575780632e0ad0041461051057806331cef44714610518578063329dbddc1461052b57806336f7ab5e1461053357600080fd5b806323b872dd146104b757806325287d42146104ca57806325d60861146104dd57806327e235e3146104f057600080fd5b8063153a1f3e116103c4578063153a1f3e1461046d57806318160ddd146104805780631935a88014610497578063200d2ed2146104aa57600080fd5b8063034f6b21146103f657806306eaa0b71461041857806306fdde031461042d5780630af7eb0f14610442575b600080fd5b6018546104039060ff1681565b60405190151581526020015b60405180910390f35b61042b610426366004612dbc565b6108cb565b005b610435610b95565b60405161040f9190612e49565b600954610455906001600160a01b031681565b6040516001600160a01b03909116815260200161040f565b61040361047b366004612f0b565b610c23565b61048960035481565b60405190815260200161040f565b61042b6104a5366004612fd2565b610e26565b6007546104039060ff1681565b6104036104c5366004612fef565b610eb8565b600454610455906001600160a01b031681565b61042b6104eb36600461302b565b61109f565b6104896104fe366004613068565b60086020526000908152604090205481565b6104356110f9565b61042b610526366004612dbc565b611106565b610435611281565b61043561128e565b61043561129b565b61042b610551366004613083565b6112a8565b61042b61056436600461309c565b611327565b61042b6105773660046130c6565b6113c5565b6104356116ac565b610489600f5481565b6104356116b9565b6104356116c6565b61042b6105ab366004612fd2565b6116d3565b61042b6105be366004612dbc565b611762565b61042b6105d1366004613083565b611ae4565b61042b6105e436600461302b565b611b33565b610435611b89565b600b546104039060ff1681565b610435611b96565b610489610614366004613068565b611ba3565b61042b610627366004613068565b611bbe565b61048960105481565b610489610643366004613068565b600d6020526000908152604090205481565b61042b61066336600461302b565b611c2a565b600054610455906001600160a01b031681565b61042b61068936600461302b565b611c80565b61042b61069c36600461302b565b611cd6565b60095461040390600160a81b900460ff1681565b6106c86106c3366004613068565b611d2c565b60405161040f9392919061311d565b610435611dd7565b61042b6106ed36600461302b565b611de4565b61042b610700366004612fd2565b611e3a565b61040361071336600461309c565b611ea2565b61042b61072636600461302b565b611f63565b60095461040390600160b01b900460ff1681565b61042b61074d36600461302b565b611fb9565b610489610760366004613145565b61200f565b61042b610773366004613178565b61203a565b61042b6107863660046130c6565b612123565b61042b610799366004612fd2565b612200565b61042b6107ac3660046131e0565b612293565b61042b6123e2565b6104896107c7366004613145565b600e60209081526000928352604080842090915290825290205481565b61042b6107f2366004612fef565b612464565b610435612632565b61048960135481565b61042b610816366004613083565b61263f565b61042b610829366004613068565b6126be565b6104356127a8565b61042b610844366004613068565b6127b5565b610435612821565b60095461040390600160a01b900460ff1681565b61042b610873366004612fd2565b61282e565b61088b610886366004613083565b612896565b604080516001600160a01b039586168152949093166020850152918301521515606082015260800161040f565b61042b6108c6366004612fef565b6128df565b336001600160a01b0316600c83815481106108e8576108e8613259565b60009182526020909120600490910201546001600160a01b03161480159061091b57506000546001600160a01b03163314155b1561095d57604080518082018252600681526531323038303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b60405180910390fd5b600c828154811061097057610970613259565b6000918252602082206003600490920201015460ff16151590036109c25760408051808201825260068152651899181c181960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b600c82815481106109d5576109d5613259565b90600052602060002090600402016002015460086000600c85815481106109fe576109fe613259565b600091825260208083206004909202909101546001600160a01b0316835282019290925260400181208054909190610a37908490613285565b9091555050600c805483908110610a5057610a50613259565b906000526020600020906004020160020154600d6000600c8581548110610a7957610a79613259565b600091825260208083206004909202909101546001600160a01b0316835282019290925260400181208054909190610ab2908490613298565b925050819055506000600c8381548110610ace57610ace613259565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817fa0cc3a4feb26502e6e6cd57b2113d58bdab80c6711d8252356be9a49945bb5f7600c8481548110610b2b57610b2b613259565b6000918252602090912060049091020154600c80546001600160a01b039092169186908110610b5c57610b5c613259565b6000918252602090912060016004909202010154604051610b8992916001600160a01b03169086906132ab565b60405180910390a25050565b60018054610ba2906132d7565b80601f0160208091040260200160405190810160405280929190818152602001828054610bce906132d7565b8015610c1b5780601f10610bf057610100808354040283529160200191610c1b565b820191906000526020600020905b815481529060010190602001808311610bfe57829003601f168201915b505050505081565b60008151835114610c6257604080518082018252600681526531323035303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b6000805b8451811015610c9e57838181518110610c8157610c81613259565b602002602001015182610c949190613285565b9150600101610c66565b5080610ca933611ba3565b1015610ce35760408051808201825260068152651899181a981960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b6004546001600160a01b03163314610d445760095460408051808201909152600681526531323035303360d01b602082015290600160a81b900460ff161515600114610d425760405162461bcd60e51b81526004016109549190612e49565b505b606060006001935060005b8651811015610e1b57610d7b878281518110610d6d57610d6d613259565b60200260200101513b151590565b15610dc457610dbd878281518110610d9557610d95613259565b6020026020010151878381518110610daf57610daf613259565b602002602001015185612a07565b9150610e04565b610e01878281518110610dd957610dd9613259565b6020026020010151878381518110610df357610df3613259565b602002602001015185612b22565b91505b811515600003610e1357600094505b600101610d4f565b505050505b92915050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610e6f5760405162461bcd60e51b81526004016109549190612e49565b50600b805460ff19168215159081179091556040519081527fe1455bc53682229469d6919bb96d7b338cbffea93170dfb5f02242c22fb6d07f906020015b60405180910390a150565b6000805460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314610f025760405162461bcd60e51b81526004016109549190612e49565b5081610f0d85611ba3565b1015610f4757604080518082018252600681526531323036303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b6060833b1561100c57610f6383610f5d87611ba3565b90612ce6565b6001600160a01b038616600090815260086020526040902055610f8f83610f8986611ba3565b90612cf9565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a90610fd490339088908790600401613311565b600060405180830381600087803b158015610fee57600080fd5b505af1158015611002573d6000803e3d6000fd5b5050505050611059565b61101983610f5d87611ba3565b6001600160a01b03861660009081526008602052604090205561103f83610f8986611ba3565b6001600160a01b0385166000908152600860205260409020555b836001600160a01b0316856001600160a01b03166000805160206134bd8339815191528560405161108c91815260200190565b60405180910390a3506001949350505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146110e85760405162461bcd60e51b81526004016109549190612e49565b5060176110f58282613389565b5050565b601a8054610ba2906132d7565b60095460408051808201909152600681526531323130303160d01b602082015290600160b01b900460ff1615156001146111535760405162461bcd60e51b81526004016109549190612e49565b50600954600160a01b900460ff1615156001036112225760095460005460405163d3da927f60e01b81523360048201526001600160a01b03918216602482015291169063d3da927f90604401602060405180830381865afa1580156111bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e09190613449565b60408051808201909152600681526518991898181960d11b60208201529015156001146112205760405162461bcd60e51b81526004016109549190612e49565b505b336000908152600a602052604090208281556002016112418282613389565b507f3bb9553f633a7ecb4cbc5f9885e750a522496ce06deff1ed9184180ca83940c033838360405161127593929190613311565b60405180910390a15050565b60118054610ba2906132d7565b60058054610ba2906132d7565b60198054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146112f15760405162461bcd60e51b81526004016109549190612e49565b5060138190556040518181527fe451abe602c33e612ea61221b5a142f7fe3c044286e42c8340ffe7fa86a5859790602001610ead565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146113705760405162461bcd60e51b81526004016109549190612e49565b506001600160a01b0382166000818152600a6020908152604091829020600101849055815192835282018390527fa42bd6bdfcb8f9b815c68e4ce0e16dec3f1e7ac53967ee8309653de522a735b19101611275565b600b5460ff1615806113e15750600954600160a81b900460ff16155b806113f35750816113f133611ba3565b105b1561142c57604080518082018252600681526531323037303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b6000546001600160a01b038481169116148015906114585750600954600160a01b900460ff1615156001145b156115175760095460005460405163d3da927f60e01b81526001600160a01b038681166004830152918216602482015291169063d3da927f90604401602060405180830381865afa1580156114b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d59190613449565b6040805180820190915260068152651899181b981960d11b60208201529015156001146115155760405162461bcd60e51b81526004016109549190612e49565b505b3360009081526008602052604081208054849290611536908490613298565b9091555050336000908152600d60205260408120805484929061155a908490613285565b9091555050600c805460408051608081018252338082526001600160a01b038881166020840190815283850189815260016060860181815290880189556000989098529351600487027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7810180549285166001600160a01b031993841617905591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c8830180549190941691161790915591517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c983015593517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8ca909101805491151560ff1990921691909117905551909182917fa256c3bc72c26fbfd6285cfde8c7e345b4d0c7ccfc725d103e9f129c5d8a039a9161169e91889088908890613466565b60405180910390a250505050565b60068054610ba2906132d7565b60178054610ba2906132d7565b601b8054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461171c5760405162461bcd60e51b81526004016109549190612e49565b506007805460ff191682151590811790915560405160ff9091161515907fe1e5c6f5867805bf3fab73f4df4b80d0e87e9262a7bc22ac7c2cb3fd5896222f90600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146117ab5760405162461bcd60e51b81526004016109549190612e49565b50600954600160a81b900460ff1615156000036117f657604080518082018252600681526531323039303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b600c828154811061180957611809613259565b6000918252602082206003600490920201015460ff161515900361185b5760408051808201825260068152651899181c981960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b600c828154811061186e5761186e613259565b90600052602060002090600402016002015460086000600c858154811061189757611897613259565b60009182526020808320600160049093020191909101546001600160a01b03168352820192909252604001812080549091906118d4908490613285565b9091555050600c8054839081106118ed576118ed613259565b906000526020600020906004020160020154600d6000600c858154811061191657611916613259565b600091825260208083206004909202909101546001600160a01b031683528201929092526040018120805490919061194f908490613298565b925050819055506000600c838154811061196b5761196b613259565b906000526020600020906004020160030160006101000a81548160ff021916908315150217905550817f6b50d6e4941ce7ae74ed7f7f51df5fb7680754c2815a094c66bea5ca80a40eb1600c84815481106119c8576119c8613259565b6000918252602090912060049091020154600c80546001600160a01b0390921691869081106119f9576119f9613259565b6000918252602090912060016004909202010154604051611a2692916001600160a01b03169086906132ab565b60405180910390a2600c8281548110611a4157611a41613259565b6000918252602090912060016004909202010154600c80546001600160a01b039092169184908110611a7557611a75613259565b6000918252602090912060049091020154600c80546001600160a01b03909216916000805160206134bd833981519152919086908110611ab757611ab7613259565b906000526020600020906004020160020154604051611ad891815260200190565b60405180910390a35050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b2d5760405162461bcd60e51b81526004016109549190612e49565b50601055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611b7c5760405162461bcd60e51b81526004016109549190612e49565b5060116110f58282613389565b60128054610ba2906132d7565b60168054610ba2906132d7565b6001600160a01b031660009081526008602052604090205490565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c075760405162461bcd60e51b81526004016109549190612e49565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611c735760405162461bcd60e51b81526004016109549190612e49565b5060196110f58282613389565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611cc95760405162461bcd60e51b81526004016109549190612e49565b5060066110f58282613389565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611d1f5760405162461bcd60e51b81526004016109549190612e49565b50601b6110f58282613389565b600a6020526000908152604090208054600182015460028301805492939192611d54906132d7565b80601f0160208091040260200160405190810160405280929190818152602001828054611d80906132d7565b8015611dcd5780601f10611da257610100808354040283529160200191611dcd565b820191906000526020600020905b815481529060010190602001808311611db057829003601f168201915b5050505050905083565b60028054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611e2d5760405162461bcd60e51b81526004016109549190612e49565b50601c6110f58282613389565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611e835760405162461bcd60e51b81526004016109549190612e49565b5060098054911515600160a81b0260ff60a81b19909216919091179055565b600081611eae33611ba3565b10156040518060400160405280600681526020016531323034303160d01b81525090611eed5760405162461bcd60e51b81526004016109549190612e49565b506009546040805180820190915260068152651899181a181960d11b602082015290600160a81b900460ff161515600114611f3b5760405162461bcd60e51b81526004016109549190612e49565b506060833b15611f5857611f50848483612a07565b915050610e20565b611f50848483612b22565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b03163314611fac5760405162461bcd60e51b81526004016109549190612e49565b50601a6110f58282613389565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146120025760405162461bcd60e51b81526004016109549190612e49565b5060056110f58282613389565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205490565b81612045338661200f565b101561207f57604080518082018252600681526518991818981960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b61208d82610f5d338761200f565b336000908152600e602090815260408083206001600160a01b03891684529091529020556120be82610f8985611ba3565b6001600160a01b038085166000908152600860205260409081902092909255905133918616907f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc9061211590879087908790613311565b60405180910390a350505050565b8161212d33611ba3565b101561216757604080518082018252600681526518991818181960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b61217482610f5d33611ba3565b33600081815260086020526040902091909155612198908390610f8990869061200f565b6001600160a01b0384166000818152600e602090815260408083203380855292529182902093909355519091907ecd77abc69e7bbb6de9df8cbfcca8720df3a30decf9eefd187e72ab2ea2f513906121f390869086906134a3565b60405180910390a3505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146122495760405162461bcd60e51b81526004016109549190612e49565b506009805460ff60b01b1916600160b01b831515908102919091179091556040517fa0c691180b8089c0c84e478ee67d6325768666cd889f13de016771c6f7af1f8890600090a250565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146122dc5760405162461bcd60e51b81526004016109549190612e49565b50816122e8868661200f565b101561232257604080518082018252600681526531323132303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b61233082610f5d878761200f565b6001600160a01b038087166000908152600e602090815260408083209389168352929052205561236382610f8985611ba3565b60086000856001600160a01b03166001600160a01b0316815260200190815260200160002081905550846001600160a01b0316846001600160a01b03167f7a1a1088c929a111dda2dc0ca1cc650aa78525d11fd3089abf20c8474f5d82cc8585856040516123d393929190613311565b60405180910390a35050505050565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b0316331461242b5760405162461bcd60e51b81526004016109549190612e49565b506018805460ff191660011790556040517f96151d7f1d8c50be29a45c0e3ae9ce2008888d1615b196e8f5ffd8c178e6dde890600090a1565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146124ad5760405162461bcd60e51b81526004016109549190612e49565b506001600160a01b0382161561254b57806124c8838561200f565b101561250257604080518082018252600681526531323131303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b61251081610f5d848661200f565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546125439082612ce6565b6003556125d6565b6001600160a01b03831660009081526008602052604090205481111561259f57604080518082018252600681526518991898981960d11b6020820152905162461bcd60e51b81526109549190600401612e49565b6125ac81610f5d85611ba3565b6001600160a01b0384166000908152600860205260409020556003546125d29082612ce6565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167fee02732fab40ece8284c756220846dff4b8d32058b86b35b4f0459bf172fcef08460405161262591815260200190565b60405180910390a4505050565b601c8054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146126885760405162461bcd60e51b81526004016109549190612e49565b50600f8190556040518181527f82b19c9a00131a87eeff1903fcb9e4f2a3ba76dd86098586eea921a533e218dc90602001610ead565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146127075760405162461bcd60e51b81526004016109549190612e49565b5060408051808201909152600681526535303031303160d01b60208201526001600160a01b03821661274c5760405162461bcd60e51b81526004016109549190612e49565b50600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60158054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146127fe5760405162461bcd60e51b81526004016109549190612e49565b50600980546001600160a01b0319166001600160a01b0392909216919091179055565b60148054610ba2906132d7565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146128775760405162461bcd60e51b81526004016109549190612e49565b5060098054911515600160a01b0260ff60a01b19909216919091179055565b600c81815481106128a657600080fd5b600091825260209091206004909102018054600182015460028301546003909301546001600160a01b0392831694509116919060ff1684565b60005460408051808201909152600681526535303030303160d01b6020820152906001600160a01b031633146129285760405162461bcd60e51b81526004016109549190612e49565b506001600160a01b038216156129815761294681610f89848661200f565b6001600160a01b038084166000908152600e60209081526040808320938816835292905220556003546129799082612cf9565b6003556129b8565b61298e81610f8985611ba3565b6001600160a01b0384166000908152600860205260409020556003546129b49082612cf9565b6003555b816001600160a01b0316836001600160a01b0316336001600160a01b03167f65adeb76912378393e600cb6f64f3310842a42e1eae86273dc775d0c47a0f2dc8460405161262591815260200190565b60045460408051808201909152600681526531323033303160d01b60208201526000916001600160a01b03868116911614612a555760405162461bcd60e51b81526004016109549190612e49565b50612a6383610f5d33611ba3565b33600090815260086020526040902055612a8083610f8986611ba3565b6001600160a01b0385166000818152600860205260409081902092909255905163607705c560e11b815285919063c0ee0b8a90612ac590339088908890600401613311565b600060405180830381600087803b158015612adf57600080fd5b505af1158015612af3573d6000803e3d6000fd5b50506040518681526001600160a01b03881692503391506000805160206134bd8339815191529060200161108c565b6004546000906001600160a01b03163314801590612b475750600b5460ff1615156001145b15612b8057604080518082018252600681526531323032303160d01b6020820152905162461bcd60e51b81526109549190600401612e49565b6000546001600160a01b03858116911614801590612bac5750600954600160a01b900460ff1615156001145b15612c6b5760095460005460405163d3da927f60e01b81526001600160a01b038781166004830152918216602482015291169063d3da927f90604401602060405180830381865afa158015612c05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c299190613449565b60408051808201909152600681526518991819181960d11b6020820152901515600114612c695760405162461bcd60e51b81526004016109549190612e49565b505b612c7883610f5d33611ba3565b33600090815260086020526040902055612c9583610f8986611ba3565b6001600160a01b0385166000818152600860205260409081902092909255905133906000805160206134bd83398151915290612cd49087815260200190565b60405180910390a35060019392505050565b6000612cf28284613298565b9392505050565b6000612cf28284613285565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612d4457612d44612d05565b604052919050565b600082601f830112612d5d57600080fd5b813567ffffffffffffffff811115612d7757612d77612d05565b612d8a601f8201601f1916602001612d1b565b818152846020838601011115612d9f57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215612dcf57600080fd5b82359150602083013567ffffffffffffffff811115612ded57600080fd5b612df985828601612d4c565b9150509250929050565b6000815180845260005b81811015612e2957602081850181015186830182015201612e0d565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612cf26020830184612e03565b600067ffffffffffffffff821115612e7657612e76612d05565b5060051b60200190565b80356001600160a01b0381168114612e9757600080fd5b919050565b600082601f830112612ead57600080fd5b81356020612ec2612ebd83612e5c565b612d1b565b8083825260208201915060208460051b870101935086841115612ee457600080fd5b602086015b84811015612f005780358352918301918301612ee9565b509695505050505050565b60008060408385031215612f1e57600080fd5b823567ffffffffffffffff80821115612f3657600080fd5b818501915085601f830112612f4a57600080fd5b81356020612f5a612ebd83612e5c565b82815260059290921b84018101918181019089841115612f7957600080fd5b948201945b83861015612f9e57612f8f86612e80565b82529482019490820190612f7e565b96505086013592505080821115612fb457600080fd5b50612df985828601612e9c565b8015158114612fcf57600080fd5b50565b600060208284031215612fe457600080fd5b8135612cf281612fc1565b60008060006060848603121561300457600080fd5b61300d84612e80565b925061301b60208501612e80565b9150604084013590509250925092565b60006020828403121561303d57600080fd5b813567ffffffffffffffff81111561305457600080fd5b61306084828501612d4c565b949350505050565b60006020828403121561307a57600080fd5b612cf282612e80565b60006020828403121561309557600080fd5b5035919050565b600080604083850312156130af57600080fd5b6130b883612e80565b946020939093013593505050565b6000806000606084860312156130db57600080fd5b6130e484612e80565b925060208401359150604084013567ffffffffffffffff81111561310757600080fd5b61311386828701612d4c565b9150509250925092565b83815282602082015260606040820152600061313c6060830184612e03565b95945050505050565b6000806040838503121561315857600080fd5b61316183612e80565b915061316f60208401612e80565b90509250929050565b6000806000806080858703121561318e57600080fd5b61319785612e80565b93506131a560208601612e80565b925060408501359150606085013567ffffffffffffffff8111156131c857600080fd5b6131d487828801612d4c565b91505092959194509250565b600080600080600060a086880312156131f857600080fd5b61320186612e80565b945061320f60208701612e80565b935061321d60408701612e80565b925060608601359150608086013567ffffffffffffffff81111561324057600080fd5b61324c88828901612d4c565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610e2057610e2061326f565b81810381811115610e2057610e2061326f565b6001600160a01b0384811682528316602082015260606040820181905260009061313c90830184612e03565b600181811c908216806132eb57607f821691505b60208210810361330b57634e487b7160e01b600052602260045260246000fd5b50919050565b60018060a01b038416815282602082015260606040820152600061313c6060830184612e03565b601f821115613384576000816000526020600020601f850160051c810160208610156133615750805b601f850160051c820191505b818110156133805782815560010161336d565b5050505b505050565b815167ffffffffffffffff8111156133a3576133a3612d05565b6133b7816133b184546132d7565b84613338565b602080601f8311600181146133ec57600084156133d45750858301515b600019600386901b1c1916600185901b178555613380565b600085815260208120601f198616915b8281101561341b578886015182559484019460019091019084016133fc565b50858210156134395787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561345b57600080fd5b8151612cf281612fc1565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061349990830184612e03565b9695505050505050565b8281526040602082015260006130606040830184612e0356feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122058ccc1e9b73794228a24d20df37b2c765313479f9d25721169fb5f5a2203353764736f6c63430008170033" } \ No newline at end of file diff --git a/contracts/contract_version.md b/contracts/contract_version.md index bbf89d63..02046028 100644 --- a/contracts/contract_version.md +++ b/contracts/contract_version.md @@ -1,23 +1,25 @@ -| Contract json file | version | bytecode used | ABI used | -|---------------------------------|--------------|-------------------------|----------| -| ContractReceiver.json | **v22.12.0** | no | yes | -| ContractRegistry.json | **v22.12.0** | no | yes | -| E2EMessaging.json | **v22.12.0** | yes(only for unittest) | yes | -| EscrowStorage.json | **v22.12.0** | yes(only for unittest) | yes | -| ExchangeStorage.json | **v22.12.0** | yes(only for unittest) | yes | -| IbetCoupon.json | **v22.12.0** | yes(only for unittest) | yes | -| IbetEscrow.json | **v22.12.0** | yes(only for unittest) | yes | -| IbetExchange.json | **v22.12.0** | yes(only for unittest) | yes | -| IbetExchangeInterface.json | **v22.12.0** | no | yes | -| IbetMembership.json | **v22.12.0** | yes(only for unittest) | yes | -| IbetSecurityTokenEscrow.json | **v22.12.0** | yes(only for unittest) | yes | -| IbetSecurityTokenInterface.json | **v22.12.0** | no | yes | -| IbetShare.json | **v22.12.0** | **yes** | yes | -| IbetStandardTokenInterface.json | **v22.12.0** | no | yes | -| IbetStandardToken.json | **v22.12.0** | yes(only for unittest) | yes | -| IbetStraightBond.json | **v23.12.0** | **yes** | yes | -| Ownable.json | **v22.12.0** | yes(only for unittest) | yes | -| PaymentGateway.json | **v22.12.0** | yes(only for unittest) | yes | -| PersonalInfo.json | **v22.12.0** | yes(only for unittest) | yes | -| TokenList.json | **v22.12.0** | yes(only for unittest) | yes | -| FreezeLog.json | **v23.12.0** | yes(only for unittest) | yes | \ No newline at end of file +| Contract json file | version | bytecode used | ABI used | +|---------------------------------|--------------|------------------------|----------| +| Ownable.json | **v22.12.0** | yes(only for unittest) | yes | +| ContractReceiver.json | **v22.12.0** | no | yes | +| ContractRegistry.json | **v22.12.0** | no | yes | +| TokenList.json | **v22.12.0** | yes(only for unittest) | yes | +| IbetStandardTokenInterface.json | **v22.12.0** | no | yes | +| IbetStandardToken.json | **v22.12.0** | yes(only for unittest) | yes | +| IbetMembership.json | **v22.12.0** | yes(only for unittest) | yes | +| IbetCoupon.json | **v22.12.0** | yes(only for unittest) | yes | +| IbetSecurityTokenInterface.json | **v24.6.0** | no | yes | +| IbetStraightBond.json | **v24.6.0** | **yes** | yes | +| IbetShare.json | **v24.6.0** | **yes** | yes | +| IbetExchangeInterface.json | **v22.12.0** | no | yes | +| IbetExchange.json | **v22.12.0** | yes(only for unittest) | yes | +| ExchangeStorage.json | **v22.12.0** | yes(only for unittest) | yes | +| IbetEscrow.json | **v22.12.0** | yes(only for unittest) | yes | +| EscrowStorage.json | **v22.12.0** | yes(only for unittest) | yes | +| IbetSecurityTokenEscrow.json | **v22.12.0** | yes(only for unittest) | yes | +| DVPStorage.json | **v24.6.0** | yes(only for unittest) | yes | +| IbetSecurityTokenDVP.json | **v24.6.0** | yes(only for unittest) | yes | +| PaymentGateway.json | **v22.12.0** | yes(only for unittest) | yes | +| PersonalInfo.json | **v22.12.0** | yes(only for unittest) | yes | +| FreezeLog.json | **v23.12.0** | yes(only for unittest) | yes | +| E2EMessaging.json | **v22.12.0** | yes(only for unittest) | yes | diff --git a/docker-compose.yml b/docker-compose.yml index 80893f1b..204aaaa5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3" - services: postgres: image: postgres:15 @@ -9,28 +7,23 @@ services: - POSTGRES_USER=apluser - POSTGRES_PASSWORD=apluserpass - POSTGRES_DB=apldb - ganache: - image: "trufflesuite/ganache:latest" + hardhat-network: + build: + context: ./ + dockerfile: tests/Dockerfile_hardhat ports: - "8545:8545" - command: - - -k - - 'berlin' - - -g - - '0' - - --chain.chainId - - '2017' ibet-prime-postgres: build: context: ./ - dockerfile: ./tests/Dockerfile + dockerfile: ./tests/Dockerfile_unittest environment: - TEST_DATABASE_URL=postgresql+psycopg://apluser:apluserpass@postgres/apldb - WEB3_HTTP_PROVIDER=http://quorum:8545 - RESPONSE_VALIDATION_MODE=1 links: - postgres:postgres - - ganache:quorum + - hardhat-network:quorum depends_on: - postgres - - ganache + - hardhat-network diff --git a/docs/generate_openapi_doc.py b/docs/generate_openapi_doc.py new file mode 100644 index 00000000..784a42f7 --- /dev/null +++ b/docs/generate_openapi_doc.py @@ -0,0 +1,64 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import os +import sys + +import typer +from ruamel.yaml import YAML + +path = os.path.join(os.path.dirname(__file__), "../") +sys.path.append(path) + +from app.main import app + + +def str_represent(dumper, data): + if len(data.splitlines()) > 1: + return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|") + return dumper.represent_scalar("tag:yaml.org,2002:str", data) + + +def none_represent(dumper, data): + return dumper.represent_scalar("tag:yaml.org,2002:null", "null") + + +def main(): + openapi_json = app.openapi() + yaml = YAML() + yaml.indent(mapping=2, sequence=4, offset=2) + yaml.representer.add_representer(str, str_represent) + yaml.representer.add_representer(type(None), none_represent) + + with open( + os.path.abspath( + os.path.join( + os.path.dirname(__file__), + os.path.pardir, + "docs/ibet_prime.yaml", + ), + ), + "w", + encoding="utf-8", + ) as f: + yaml.dump(openapi_json, f) + + +if __name__ == "__main__": + typer.run(main) diff --git a/docs/ibet_prime.yaml b/docs/ibet_prime.yaml new file mode 100644 index 00000000..ced5636b --- /dev/null +++ b/docs/ibet_prime.yaml @@ -0,0 +1,13975 @@ +openapi: 3.1.0 +info: + title: ibet Prime + description: Security token management system for ibet network + version: '24.6' +paths: + /: + get: + tags: + - root + summary: Root + operationId: root__get + responses: + '200': + description: Successful Response + /e2ee: + get: + tags: + - common + summary: E2E Encryption Key + description: Get E2EE public key + operationId: e2e_encryption_key_e2ee_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/E2EEResponse' + /healthcheck: + get: + tags: + - common + summary: Check Health + operationId: check_health_healthcheck_get + responses: + '200': + description: Successful Response + '503': + description: Service Unavailable Error + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceUnavailableErrorResponse' + /block_number: + get: + tags: + - common + summary: Get Block Number + description: Get the latest block number + operationId: get_block_number_block_number_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BlockNumberResponse' + '503': + description: Service Unavailable Error + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceUnavailableErrorResponse' + /accounts: + get: + tags: + - account + summary: List All Accounts + description: List all accounts + operationId: list_all_accounts_accounts_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/AccountResponse' + type: array + title: Response List All Accounts Accounts Get + post: + tags: + - account + summary: Create Key + description: Create Keys + operationId: create_key_accounts_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AccountCreateKeyRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /accounts/{issuer_address}: + get: + tags: + - account + summary: Retrieve Account + description: Retrieve an account + operationId: retrieve_account_accounts__issuer_address__get + parameters: + - name: issuer_address + in: path + required: true + schema: + type: string + title: Issuer Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + delete: + tags: + - account + summary: Delete Account + description: Logically delete an account + operationId: delete_account_accounts__issuer_address__delete + parameters: + - name: issuer_address + in: path + required: true + schema: + type: string + title: Issuer Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /accounts/{issuer_address}/rsakey: + post: + tags: + - account + summary: Generate Rsa Key + description: Generate RSA key + operationId: generate_rsa_key_accounts__issuer_address__rsakey_post + parameters: + - name: issuer_address + in: path + required: true + schema: + type: string + title: Issuer Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountGenerateRsaKeyRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /accounts/{issuer_address}/eoa_password: + post: + tags: + - account + summary: Change Eoa Password + description: Change EOA Password + operationId: change_eoa_password_accounts__issuer_address__eoa_password_post + parameters: + - name: issuer_address + in: path + required: true + schema: + type: string + title: Issuer Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountChangeEOAPasswordRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /accounts/{issuer_address}/rsa_passphrase: + post: + tags: + - account + summary: Change Rsa Passphrase + description: Change RSA Passphrase + operationId: change_rsa_passphrase_accounts__issuer_address__rsa_passphrase_post + parameters: + - name: issuer_address + in: path + required: true + schema: + type: string + title: Issuer Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountChangeRSAPassphraseRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /accounts/{issuer_address}/auth_token: + post: + tags: + - account + summary: Create Auth Token + description: Create Auth Token + operationId: create_auth_token_accounts__issuer_address__auth_token_post + parameters: + - name: issuer_address + in: path + required: true + schema: + type: string + title: Issuer Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountAuthTokenRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AccountAuthTokenResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthTokenAlreadyExistsErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + title: Response 400 Create Auth Token Accounts Issuer Address Auth + Token Post + delete: + tags: + - account + summary: Delete Auth Token + description: Delete auth token + operationId: delete_auth_token_accounts__issuer_address__auth_token_delete + parameters: + - name: issuer_address + in: path + required: true + schema: + type: string + title: Issuer Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '401': + description: Authorization Error + content: + application/json: + schema: + $ref: '#/components/schemas/AuthorizationErrorResponse' + /bond/tokens: + post: + tags: + - bond + summary: Issue Token + description: Issue ibetStraightBond token + operationId: issue_token_bond_tokens_post + parameters: + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetStraightBondCreate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TokenAddressResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Issue Token Bond Tokens Post + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Issue Token Bond Tokens Post + get: + tags: + - bond + summary: List All Tokens + description: List all issued tokens + operationId: list_all_tokens_bond_tokens_get + parameters: + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IbetStraightBondResponse' + title: Response List All Tokens Bond Tokens Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /bond/tokens/{token_address}: + get: + tags: + - bond + summary: Retrieve Token + description: Retrieve token + operationId: retrieve_token_bond_tokens__token_address__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IbetStraightBondResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - bond + summary: Update Token + description: Update a token + operationId: update_token_bond_tokens__token_address__post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetStraightBondUpdate' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Update Token Bond Tokens Token Address Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/OperationNotSupportedVersionErrorResponse' + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Update Token Bond Tokens Token Address Post + /bond/tokens/{token_address}/history: + get: + tags: + - bond + summary: List Bond Operation Log History + description: List of token operation log history + operationId: list_bond_operation_log_history_bond_tokens__token_address__history_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: modified_contents + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Modified contents query + title: Modified Contents + description: Modified contents query + - name: operation_category + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/TokenUpdateOperationCategory' + - type: 'null' + description: Trigger of change + title: Operation Category + description: Trigger of change + - name: created_from + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: created datetime (From) + title: Created From + description: created datetime (From) + - name: created_to + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: created datetime (To) + title: Created To + description: created datetime (To) + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListTokenHistorySortItem' + description: Sort item + default: created + title: Sort Item + description: Sort item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 'Sort order(0: ASC, 1: DESC)' + default: 1 + title: Sort Order + description: 'Sort order(0: ASC, 1: DESC)' + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListTokenOperationLogHistoryResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /bond/tokens/{token_address}/additional_issue: + get: + tags: + - bond + summary: List Additional Issuance History + description: List additional issuance history + operationId: + list_additional_issuance_history_bond_tokens__token_address__additional_issue_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/IssueRedeemSortItem' + default: block_timestamp + title: Sort Item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IssueRedeemHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - bond + summary: Additional Issue + description: Additional issue + operationId: additional_issue_bond_tokens__token_address__additional_issue_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetStraightBondAdditionalIssue' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Additional Issue Bond Tokens Token Address Additional + Issue Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Additional Issue Bond Tokens Token Address Additional + Issue Post + /bond/tokens/{token_address}/additional_issue/batch: + get: + tags: + - bond + summary: List All Additional Issue Upload + operationId: + list_all_additional_issue_upload_bond_tokens__token_address__additional_issue_batch_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: processed + in: query + required: false + schema: + anyOf: + - type: boolean + - type: 'null' + title: Processed + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListBatchIssueRedeemUploadResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + post: + tags: + - bond + summary: Additional Issue In Batch + description: Additional issue (Batch) + operationId: + additional_issue_in_batch_bond_tokens__token_address__additional_issue_batch_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IbetStraightBondAdditionalIssue' + title: Data + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BatchIssueRedeemUploadIdResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Additional Issue In Batch Bond Tokens Token Address Additional + Issue Batch Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /bond/tokens/{token_address}/additional_issue/batch/{batch_id}: + get: + tags: + - bond + summary: Retrieve Batch Additional Issue + description: Get Batch status for additional issue + operationId: + retrieve_batch_additional_issue_bond_tokens__token_address__additional_issue_batch__batch_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: batch_id + in: path + required: true + schema: + type: string + title: Batch Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GetBatchIssueRedeemResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /bond/tokens/{token_address}/redeem: + get: + tags: + - bond + summary: List Redeem History + description: List redemption history + operationId: list_redeem_history_bond_tokens__token_address__redeem_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/IssueRedeemSortItem' + default: block_timestamp + title: Sort Item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IssueRedeemHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - bond + summary: Redeem Token + description: Redeem a token + operationId: redeem_token_bond_tokens__token_address__redeem_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetStraightBondRedeem' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Redeem Token Bond Tokens Token Address Redeem + Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Redeem Token Bond Tokens Token Address Redeem + Post + /bond/tokens/{token_address}/redeem/batch: + get: + tags: + - bond + summary: List All Redeem Upload + operationId: list_all_redeem_upload_bond_tokens__token_address__redeem_batch_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: processed + in: query + required: false + schema: + anyOf: + - type: boolean + - type: 'null' + title: Processed + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListBatchIssueRedeemUploadResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + post: + tags: + - bond + summary: Redeem Token In Batch + description: Redeem a token (Batch) + operationId: redeem_token_in_batch_bond_tokens__token_address__redeem_batch_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IbetStraightBondRedeem' + title: Data + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BatchIssueRedeemUploadIdResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Redeem Token In Batch Bond Tokens Token Address Redeem + Batch Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /bond/tokens/{token_address}/redeem/batch/{batch_id}: + get: + tags: + - bond + summary: Retrieve Batch Redeem + description: Get Batch status for additional issue + operationId: retrieve_batch_redeem_bond_tokens__token_address__redeem_batch__batch_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: batch_id + in: path + required: true + schema: + type: string + title: Batch Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GetBatchIssueRedeemResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /bond/tokens/{token_address}/scheduled_events: + get: + tags: + - bond + summary: List All Scheduled Events + description: List all scheduled update events + operationId: list_all_scheduled_events_bond_tokens__token_address__scheduled_events_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ScheduledEventResponse' + title: Response List All Scheduled Events Bond Tokens Token Address Scheduled + Events Get + post: + tags: + - bond + summary: Schedule New Update Event + description: Register a new update event + operationId: schedule_new_update_event_bond_tokens__token_address__scheduled_events_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetStraightBondScheduledUpdate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScheduledEventIdResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Schedule New Update Event Bond Tokens Token Address Scheduled + Events Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/OperationNotSupportedVersionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + title: Response 400 Schedule New Update Event Bond Tokens Token Address Scheduled + Events Post + /bond/tokens/{token_address}/scheduled_events/{scheduled_event_id}: + get: + tags: + - bond + summary: Retrieve Token Event + description: Retrieve a scheduled token event + operationId: + retrieve_token_event_bond_tokens__token_address__scheduled_events__scheduled_event_id__get + parameters: + - name: scheduled_event_id + in: path + required: true + schema: + type: string + title: Scheduled Event Id + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScheduledEventResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + delete: + tags: + - bond + summary: Delete Scheduled Event + description: Delete a scheduled event + operationId: + delete_scheduled_event_bond_tokens__token_address__scheduled_events__scheduled_event_id__delete + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: scheduled_event_id + in: path + required: true + schema: + type: string + title: Scheduled Event Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScheduledEventResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Delete Scheduled Event Bond Tokens Token Address Scheduled + Events Scheduled Event Id Delete + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /bond/tokens/{token_address}/holders: + get: + tags: + - bond + summary: List All Holders + description: List all bond token holders + operationId: list_all_holders_bond_tokens__token_address__holders_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: include_former_holder + in: query + required: false + schema: + type: boolean + default: false + title: Include Former Holder + - name: balance + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: number of balance + title: Balance + description: number of balance + - name: balance_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: search condition of balance(0:equal, 1:greater than or equal, + 2:less than or equal) + default: 0 + title: Balance Operator + description: search condition of balance(0:equal, 1:greater than or equal, + 2:less than or equal) + - name: pending_transfer + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: number of pending transfer amount + title: Pending Transfer + description: number of pending transfer amount + - name: pending_transfer_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: search condition of pending transfer(0:equal, 1:greater than + or equal, 2:less than or equal) + default: 0 + title: Pending Transfer Operator + description: search condition of pending transfer(0:equal, 1:greater than + or equal, 2:less than or equal) + - name: locked + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: number of locked amount + title: Locked + description: number of locked amount + - name: locked_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: search condition of locked amount(0:equal, 1:greater than + or equal, 2:less than or equal) + default: 0 + title: Locked Operator + description: search condition of locked amount(0:equal, 1:greater than or + equal, 2:less than or equal) + - name: balance_and_pending_transfer + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: number of balance plus pending transfer amount + title: Balance And Pending Transfer + description: number of balance plus pending transfer amount + - name: balance_and_pending_transfer_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: search condition of balance plus pending transfer(0:equal, + 1:greater than or equal, 2:less than or equal) + default: 0 + title: Balance And Pending Transfer Operator + description: search condition of balance plus pending transfer(0:equal, + 1:greater than or equal, 2:less than or equal) + - name: account_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: account address(partial match) + title: Account Address + description: account address(partial match) + - name: holder_name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: holder name(partial match) + title: Holder Name + description: holder name(partial match) + - name: key_manager + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: key manager(partial match) + title: Key Manager + description: key manager(partial match) + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListAllHoldersSortItem' + description: Sort Item + default: created + title: Sort Item + description: Sort Item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 0 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HoldersResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /bond/tokens/{token_address}/holders/count: + get: + tags: + - bond + summary: Count Number Of Holders + description: Count the number of holders + operationId: count_number_of_holders_bond_tokens__token_address__holders_count_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HolderCountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /bond/tokens/{token_address}/holders/{account_address}: + get: + tags: + - bond + summary: Retrieve Holder + description: Retrieve bond token holder + operationId: retrieve_holder_bond_tokens__token_address__holders__account_address__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HolderResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /bond/tokens/{token_address}/personal_info: + post: + tags: + - bond + summary: Register Holder Personal Info + description: Register the holder's personal information + operationId: register_holder_personal_info_bond_tokens__token_address__personal_info_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterPersonalInfoRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Register Holder Personal Info Bond Tokens Token + Address Personal Info Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Register Holder Personal Info Bond Tokens Token + Address Personal Info Post + /bond/tokens/{token_address}/personal_info/batch: + get: + tags: + - bond + summary: List All Personal Info Batch Registration Uploads + description: List all personal information batch registration uploads + operationId: + list_all_personal_info_batch_registration_uploads_bond_tokens__token_address__personal_info_batch_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: status + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Status + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListBatchRegisterPersonalInfoUploadResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - bond + summary: Batch Register Personal Info + description: Create Batch for register personal information + operationId: + batch_register_personal_info_bond_tokens__token_address__personal_info_batch_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RegisterPersonalInfoRequest' + title: Personal Info List + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BatchRegisterPersonalInfoUploadResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Batch Register Personal Info Bond Tokens Token + Address Personal Info Batch Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /bond/tokens/{token_address}/personal_info/batch/{batch_id}: + get: + tags: + - bond + summary: Retrieve Batch Register Personal Info + description: Get Batch status for register personal information + operationId: + retrieve_batch_register_personal_info_bond_tokens__token_address__personal_info_batch__batch_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: batch_id + in: path + required: true + schema: + type: string + title: Batch Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GetBatchRegisterPersonalInfoResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error401Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /bond/tokens/{token_address}/lock_events: + get: + tags: + - bond + summary: List all lock/unlock events related to given bond token + operationId: list_all_lock_events_by_bond_bond_tokens__token_address__lock_events_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: account_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Account address + title: Account Address + description: Account address + - name: msg_sender + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Msg sender + title: Msg Sender + description: Msg sender + - name: lock_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Lock address + title: Lock Address + description: Lock address + - name: recipient_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Recipient address + title: Recipient Address + description: Recipient address + - name: category + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/LockEventCategory' + - type: 'null' + description: Event category + title: Category + description: Event category + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListAllTokenLockEventsSortItem' + description: Sort item + default: block_timestamp + title: Sort Item + description: Sort item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 'Sort order(0: ASC, 1: DESC)' + default: 1 + title: Sort Order + description: 'Sort order(0: ASC, 1: DESC)' + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllTokenLockEventsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /bond/transfers: + post: + tags: + - bond + summary: Transfer Ownership + description: Transfer token ownership + operationId: transfer_ownership_bond_transfers_post + parameters: + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetStraightBondTransfer' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Transfer Ownership Bond Transfers Post + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Transfer Ownership Bond Transfers Post + /bond/transfers/{token_address}: + get: + tags: + - bond + summary: List Transfer History + description: List token transfer history + operationId: list_transfer_history_bond_transfers__token_address__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: block_timestamp_from + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + description: block timestamp (From) + title: Block Timestamp From + description: block timestamp (From) + - name: block_timestamp_to + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + descriptio0n: block timestamp (To) + title: Block Timestamp To + - name: from_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: transfer source address + title: From Address + description: transfer source address + - name: to_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: transfer destination address + title: To Address + description: transfer destination address + - name: from_address_name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: name of transfer source address + title: From Address Name + description: name of transfer source address + - name: to_address_name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: name of transfer destination address + title: To Address Name + description: name of transfer destination address + - name: amount + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: transfer amount + title: Amount + description: transfer amount + - name: amount_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: 'value filter condition(0: equal, 1: greater than, 2: less + than)' + default: 0 + title: Amount Operator + description: 'value filter condition(0: equal, 1: greater than, 2: less + than)' + - name: source_event + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/TransferSourceEventType' + - type: 'null' + description: source event of transfer + title: Source Event + description: source event of transfer + - name: data + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: source event data + title: Data + description: source event data + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListTransferHistorySortItem' + description: sort item + default: block_timestamp + title: Sort Item + description: sort item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: start position + title: Offset + description: start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: number of set + title: Limit + description: number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /bond/transfer_approvals: + get: + tags: + - bond + summary: List Transfer Approval History + description: List transfer approval history + operationId: list_transfer_approval_history_bond_transfer_approvals_get + parameters: + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Offset + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferApprovalsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /bond/transfer_approvals/{token_address}: + get: + tags: + - bond + summary: List Token Transfer Approval History + description: List token transfer approval history + operationId: list_token_transfer_approval_history_bond_transfer_approvals__token_address__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: from_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: From Address + - name: to_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: To Address + - name: status + in: query + required: false + schema: + anyOf: + - type: array + items: + $ref: '#/components/schemas/TransferApprovalStatus' + - type: 'null' + description: 0:unapproved, 1:escrow_finished, 2:transferred, 3:canceled + title: Status + description: 0:unapproved, 1:escrow_finished, 2:transferred, 3:canceled + - name: sort_item + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ListTransferApprovalHistorySortItem' + - type: 'null' + default: id + title: Sort Item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: start position + title: Offset + description: start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: number of set + title: Limit + description: number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferApprovalHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /bond/transfer_approvals/{token_address}/{id}: + post: + tags: + - bond + summary: Update Transfer Approval + description: Update on the status of a bond transfer approval + operationId: update_transfer_approval_bond_transfer_approvals__token_address___id__post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: id + in: path + required: true + schema: + type: integer + title: Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateTransferApprovalRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Update Transfer Approval Bond Transfer Approvals Token + Address Id Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/OperationNotAllowedStateErrorResponse' + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Update Transfer Approval Bond Transfer Approvals Token + Address Id Post + get: + tags: + - bond + summary: Retrieve Transfer Approval History + description: Retrieve bond token transfer approval history + operationId: + retrieve_transfer_approval_history_bond_transfer_approvals__token_address___id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: id + in: path + required: true + schema: + type: integer + title: Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferApprovalTokenDetailResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /bond/bulk_transfer: + post: + tags: + - bond + summary: Bulk Transfer Ownership + description: |- + Bulk transfer token ownership + + By using "transaction compression mode", it is possible to consolidate multiple transfers into one transaction. + This speeds up the time it takes for all transfers to be completed. + On the other hand, when using transaction compression, the input data must meet the following conditions. + - All `token_address` must be the same. + - All `from_address` must be the same. + - `from_address` and `issuer_address` must be the same. + operationId: bulk_transfer_ownership_bond_bulk_transfer_post + parameters: + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetStraightBondBulkTransferRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BulkTransferUploadIdResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Bulk Transfer Ownership Bond Bulk Transfer Post + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + get: + tags: + - bond + summary: List Bulk Transfer Upload + description: List bulk transfer uploads + operationId: list_bulk_transfer_upload_bond_bulk_transfer_get + parameters: + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BulkTransferUploadResponse' + title: Response List Bulk Transfer Upload Bond Bulk Transfer Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /bond/bulk_transfer/{upload_id}: + get: + tags: + - bond + summary: Retrieve Bulk Transfer + description: Retrieve a bulk transfer upload + operationId: retrieve_bulk_transfer_bond_bulk_transfer__upload_id__get + parameters: + - name: upload_id + in: path + required: true + schema: + type: string + title: Upload Id + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BulkTransferResponse' + title: Response Retrieve Bulk Transfer Bond Bulk Transfer Upload + Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /e2e_messaging/accounts: + get: + tags: + - messaging + summary: List All Accounts + description: List all e2e messaging accounts + operationId: list_all_accounts_e2e_messaging_accounts_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: + items: + $ref: '#/components/schemas/E2EMessagingAccountResponse' + type: array + title: Response List All Accounts E2E Messaging Accounts Get + post: + tags: + - messaging + summary: Create Account + description: Create Account + operationId: create_account_e2e_messaging_accounts_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingAccountCreateRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingAccountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Create Account E2E Messaging Accounts Post + /e2e_messaging/accounts/{account_address}: + get: + tags: + - messaging + summary: Retrieve Account + description: Retrieve an e2e messaging account + operationId: retrieve_account_e2e_messaging_accounts__account_address__get + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingAccountResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + delete: + tags: + - messaging + summary: Delete Account + description: Logically delete an e2e messaging account + operationId: delete_account_e2e_messaging_accounts__account_address__delete + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingAccountResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /e2e_messaging/accounts/{account_address}/rsa_key: + post: + tags: + - messaging + summary: Update Account Rsa Key + description: Update an e2e messaging account rsa key + operationId: update_account_rsa_key_e2e_messaging_accounts__account_address__rsa_key_post + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingAccountUpdateRsaKeyRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingAccountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /e2e_messaging/accounts/{account_address}/eoa_password: + post: + tags: + - messaging + summary: Change Eoa Password + description: Change Account's EOA Password + operationId: change_eoa_password_e2e_messaging_accounts__account_address__eoa_password_post + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingAccountChangeEOAPasswordRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /e2e_messaging/accounts/{account_address}/rsa_passphrase: + post: + tags: + - messaging + summary: Change Rsa Passphrase + description: Change Account's RSA Passphrase + operationId: + change_rsa_passphrase_e2e_messaging_accounts__account_address__rsa_passphrase_post + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingAccountChangeRSAPassphraseRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /e2e_messaging/messages: + get: + tags: + - messaging + summary: List All E2E Messages + description: List all e2e message + operationId: list_all_e2e_messages_e2e_messaging_messages_get + parameters: + - name: from_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: From Address + - name: to_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: To Address + - name: type + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Type + - name: message + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: partial match + title: Message + description: partial match + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Offset + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Limit + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllE2EMessagingResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /e2e_messaging/messages/{id}: + get: + tags: + - messaging + summary: Retrieve E2E Messaging + description: Retrieve an e2e message + operationId: retrieve_e2e_messaging_e2e_messaging_messages__id__get + parameters: + - name: id + in: path + required: true + schema: + type: integer + title: Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/E2EMessagingResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /files: + get: + tags: + - utility + summary: List All Upload Files + description: List all files + operationId: list_all_upload_files_files_get + parameters: + - name: relation + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Relation + - name: file_name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: partial match + title: File Name + description: partial match + - name: label + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: partial match + title: Label + description: partial match + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Offset + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllFilesResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + post: + tags: + - utility + summary: Upload File + description: Upload file + operationId: upload_file_files_post + parameters: + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UploadFileRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/FileResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /files/{file_id}: + get: + tags: + - utility + summary: Download File + description: Download file + operationId: download_file_files__file_id__get + parameters: + - name: file_id + in: path + required: true + schema: + type: string + title: File Id + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/DownloadFileResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + delete: + tags: + - utility + summary: Delete File + description: Delete file + operationId: delete_file_files__file_id__delete + parameters: + - name: file_id + in: path + required: true + schema: + type: string + title: File Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /ledger/{token_address}/history: + get: + tags: + - token_common + summary: List All Ledger History + description: List all Ledger + operationId: list_all_ledger_history_ledger__token_address__history_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: offset + in: query + required: false + schema: + type: integer + title: Offset + - name: limit + in: query + required: false + schema: + type: integer + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllLedgerHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /ledger/{token_address}/history/{ledger_id}: + get: + tags: + - token_common + summary: Retrieve Ledger History + description: Retrieve Ledger + operationId: retrieve_ledger_history_ledger__token_address__history__ledger_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: ledger_id + in: path + required: true + schema: + type: integer + title: Ledger Id + - name: latest_flg + in: query + required: true + schema: + type: integer + maximum: 1 + minimum: 0 + title: Latest Flg + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/RetrieveLedgerHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/Integer64bitLimitExceededErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + title: Response 400 Retrieve Ledger History Ledger Token Address History Ledger + Id Get + /ledger/{token_address}/template: + get: + tags: + - token_common + summary: Retrieve Ledger Template + description: Retrieve Ledger Template + operationId: retrieve_ledger_template_ledger__token_address__template_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/LedgerTemplateResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - token_common + summary: Create Update Ledger Template + description: Create or Update Ledger Template + operationId: create_update_ledger_template_ledger__token_address__template_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUpdateLedgerTemplateRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + delete: + tags: + - token_common + summary: Delete Ledger Template + description: Delete Ledger Template + operationId: delete_ledger_template_ledger__token_address__template_delete + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /ledger/{token_address}/details_data: + get: + tags: + - token_common + summary: List All Ledger Details Data + description: List all Ledger Details Data + operationId: list_all_ledger_details_data_ledger__token_address__details_data_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: offset + in: query + required: false + schema: + type: integer + title: Offset + - name: limit + in: query + required: false + schema: + type: integer + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllLedgerDetailsDataResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - token_common + summary: Create Ledger Details Data + description: Create Ledger Details Data + operationId: create_ledger_details_data_ledger__token_address__details_data_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CreateUpdateLedgerDetailsDataRequest' + title: Data List + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/LedgerDetailsDataResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /ledger/{token_address}/details_data/{data_id}: + get: + tags: + - token_common + summary: Retrieve Ledger Details Data + description: Retrieve Ledger Details Data + operationId: retrieve_ledger_details_data_ledger__token_address__details_data__data_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: data_id + in: path + required: true + schema: + type: string + title: Data Id + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RetrieveLedgerDetailsDataResponse' + title: Response Retrieve Ledger Details Data Ledger Token Address Details + Data Data Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - token_common + summary: Update Ledger Details Data + description: Update Ledger Details Data + operationId: update_ledger_details_data_ledger__token_address__details_data__data_id__post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: data_id + in: path + required: true + schema: + type: string + title: Data Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CreateUpdateLedgerDetailsDataRequest' + title: Data List + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + delete: + tags: + - token_common + summary: Delete Ledger Details Data + description: Delete Ledger Details Data + operationId: delete_ledger_details_data_ledger__token_address__details_data__data_id__delete + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: data_id + in: path + required: true + schema: + type: string + title: Data Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /notifications: + get: + tags: + - notification + summary: List All Notifications + description: List all notifications + operationId: list_all_notifications_notifications_get + parameters: + - name: notice_type + in: query + required: false + schema: + type: string + title: Notice Type + - name: offset + in: query + required: false + schema: + type: integer + title: Offset + - name: limit + in: query + required: false + schema: + type: integer + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllNotificationsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /notifications/{notice_id}: + delete: + tags: + - notification + summary: Delete Notification + description: Delete notification + operationId: delete_notification_notifications__notice_id__delete + parameters: + - name: notice_id + in: path + required: true + schema: + type: string + title: Notice Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /positions/{account_address}: + get: + tags: + - token_common + summary: List all positions in the account + description: List all account's position + operationId: list_all_position_positions__account_address__get + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + - name: include_former_position + in: query + required: false + schema: + type: boolean + default: false + title: Include Former Position + - name: token_type + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/TokenType' + - type: 'null' + title: Token Type + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Offset + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllPositionResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /positions/{account_address}/lock: + get: + tags: + - token_common + summary: List all locked positions in the account + description: List all account's locked position + operationId: list_all_locked_position_positions__account_address__lock_get + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + - name: token_type + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/TokenType' + - type: 'null' + title: Token Type + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Offset + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllLockedPositionResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /positions/{account_address}/lock/events: + get: + tags: + - token_common + summary: List all lock/unlock events in the account + description: List all lock/unlock events in the account + operationId: list_all_lock_events_positions__account_address__lock_events_get + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: token_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Token address + title: Token Address + description: Token address + - name: token_type + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/TokenType' + - type: 'null' + description: Token type + title: Token Type + description: Token type + - name: msg_sender + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Msg sender + title: Msg Sender + description: Msg sender + - name: lock_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Lock address + title: Lock Address + description: Lock address + - name: recipient_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Recipient address + title: Recipient Address + description: Recipient address + - name: category + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/LockEventCategory' + - type: 'null' + description: Event category + title: Category + description: Event category + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListAllLockEventsSortItem' + description: Sort item + default: block_timestamp + title: Sort Item + description: Sort item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 'Sort order(0: ASC, 1: DESC)' + default: 1 + title: Sort Order + description: 'Sort order(0: ASC, 1: DESC)' + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllLockEventsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /positions/{account_address}/force_unlock: + post: + tags: + - token_common + summary: Force unlock the locked position + description: Force unlock the locked position + operationId: force_unlock_positions__account_address__force_unlock_post + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ForceUnlockRequest' + responses: + '200': + description: Successful Response + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Force Unlock Positions Account Address Force + Unlock Post + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Force Unlock Positions Account Address Force + Unlock Post + /positions/{account_address}/{token_address}: + get: + tags: + - token_common + summary: Token position in the account + description: Retrieve account's position + operationId: retrieve_position_positions__account_address___token_address__get + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/PositionResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /share/tokens: + post: + tags: + - share + summary: Issue Token + description: Issue ibetShare token + operationId: issue_token_share_tokens_post + parameters: + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetShareCreate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TokenAddressResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Issue Token Share Tokens Post + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Issue Token Share Tokens Post + get: + tags: + - share + summary: List All Tokens + operationId: list_all_tokens_share_tokens_get + parameters: + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IbetShareResponse' + title: Response List All Tokens Share Tokens Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /share/tokens/{token_address}: + get: + tags: + - share + summary: Retrieve Token + description: Retrieve token + operationId: retrieve_token_share_tokens__token_address__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IbetShareResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - share + summary: Update Token + description: Update a token + operationId: update_token_share_tokens__token_address__post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetShareUpdate' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Update Token Share Tokens Token Address Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/OperationNotSupportedVersionErrorResponse' + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Update Token Share Tokens Token Address Post + /share/tokens/{token_address}/history: + get: + tags: + - share + summary: List Share Operation Log History + description: List of token operation log history + operationId: list_share_operation_log_history_share_tokens__token_address__history_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: modified_contents + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Modified contents query + title: Modified Contents + description: Modified contents query + - name: operation_category + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/TokenUpdateOperationCategory' + - type: 'null' + description: Trigger of change + title: Operation Category + description: Trigger of change + - name: created_from + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: created datetime (From) + title: Created From + description: created datetime (From) + - name: created_to + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: created datetime (To) + title: Created To + description: created datetime (To) + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListTokenHistorySortItem' + description: Sort item + default: created + title: Sort Item + description: Sort item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 'Sort order(0: ASC, 1: DESC)' + default: 1 + title: Sort Order + description: 'Sort order(0: ASC, 1: DESC)' + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListTokenOperationLogHistoryResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /share/tokens/{token_address}/additional_issue: + get: + tags: + - share + summary: List Additional Issuance History + description: List additional issuance history + operationId: + list_additional_issuance_history_share_tokens__token_address__additional_issue_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/IssueRedeemSortItem' + default: block_timestamp + title: Sort Item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IssueRedeemHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - share + summary: Additional Issue + description: Additional issue + operationId: additional_issue_share_tokens__token_address__additional_issue_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetShareAdditionalIssue' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Additional Issue Share Tokens Token Address Additional + Issue Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Additional Issue Share Tokens Token Address Additional + Issue Post + /share/tokens/{token_address}/additional_issue/batch: + get: + tags: + - share + summary: List All Additional Issue Upload + operationId: + list_all_additional_issue_upload_share_tokens__token_address__additional_issue_batch_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: processed + in: query + required: false + schema: + anyOf: + - type: boolean + - type: 'null' + title: Processed + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListBatchIssueRedeemUploadResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + post: + tags: + - share + summary: Additional Issue In Batch + description: Additional issue (Batch) + operationId: + additional_issue_in_batch_share_tokens__token_address__additional_issue_batch_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IbetShareAdditionalIssue' + title: Data + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BatchIssueRedeemUploadIdResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Additional Issue In Batch Share Tokens Token + Address Additional Issue Batch Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /share/tokens/{token_address}/additional_issue/batch/{batch_id}: + get: + tags: + - share + summary: Retrieve Batch Additional Issue + description: Get Batch status for additional issue + operationId: + retrieve_batch_additional_issue_share_tokens__token_address__additional_issue_batch__batch_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: batch_id + in: path + required: true + schema: + type: string + title: Batch Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GetBatchIssueRedeemResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /share/tokens/{token_address}/redeem: + get: + tags: + - share + summary: List Redeem History + description: List redemption history + operationId: list_redeem_history_share_tokens__token_address__redeem_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/IssueRedeemSortItem' + default: block_timestamp + title: Sort Item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/IssueRedeemHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - share + summary: Redeem Token + description: Redeem a token + operationId: redeem_token_share_tokens__token_address__redeem_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetShareRedeem' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Redeem Token Share Tokens Token Address Redeem + Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Redeem Token Share Tokens Token Address Redeem + Post + /share/tokens/{token_address}/redeem/batch: + get: + tags: + - share + summary: List All Redeem Upload + operationId: list_all_redeem_upload_share_tokens__token_address__redeem_batch_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: processed + in: query + required: false + schema: + anyOf: + - type: boolean + - type: 'null' + title: Processed + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListBatchIssueRedeemUploadResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + post: + tags: + - share + summary: Redeem Token In Batch + description: Redeem a token (Batch) + operationId: redeem_token_in_batch_share_tokens__token_address__redeem_batch_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IbetShareRedeem' + title: Data + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BatchIssueRedeemUploadIdResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Redeem Token In Batch Share Tokens Token Address Redeem + Batch Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /share/tokens/{token_address}/redeem/batch/{batch_id}: + get: + tags: + - share + summary: Retrieve Batch Redeem + description: Get Batch status for additional issue + operationId: retrieve_batch_redeem_share_tokens__token_address__redeem_batch__batch_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: batch_id + in: path + required: true + schema: + type: string + title: Batch Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GetBatchIssueRedeemResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /share/tokens/{token_address}/scheduled_events: + get: + tags: + - share + summary: List All Scheduled Events + description: List all scheduled update events + operationId: list_all_scheduled_events_share_tokens__token_address__scheduled_events_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ScheduledEventResponse' + title: Response List All Scheduled Events Share Tokens Token Address Scheduled + Events Get + post: + tags: + - share + summary: Schedule New Update Event + description: Register a new update event + operationId: schedule_new_update_event_share_tokens__token_address__scheduled_events_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetShareScheduledUpdate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScheduledEventIdResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Schedule New Update Event Share Tokens Token + Address Scheduled Events Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/OperationNotSupportedVersionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + title: Response 400 Schedule New Update Event Share Tokens Token + Address Scheduled Events Post + /share/tokens/{token_address}/scheduled_events/{scheduled_event_id}: + get: + tags: + - share + summary: Retrieve Token Event + description: Retrieve a scheduled token event + operationId: + retrieve_token_event_share_tokens__token_address__scheduled_events__scheduled_event_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: scheduled_event_id + in: path + required: true + schema: + type: string + title: Scheduled Event Id + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScheduledEventResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + delete: + tags: + - share + summary: Delete Scheduled Event + description: Delete a scheduled event + operationId: + delete_scheduled_event_share_tokens__token_address__scheduled_events__scheduled_event_id__delete + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: scheduled_event_id + in: path + required: true + schema: + type: string + title: Scheduled Event Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ScheduledEventResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Delete Scheduled Event Share Tokens Token Address Scheduled + Events Scheduled Event Id Delete + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /share/tokens/{token_address}/holders: + get: + tags: + - share + summary: List All Holders + description: List all share token holders + operationId: list_all_holders_share_tokens__token_address__holders_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: include_former_holder + in: query + required: false + schema: + type: boolean + default: false + title: Include Former Holder + - name: balance + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: number of balance + title: Balance + description: number of balance + - name: balance_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: search condition of balance(0:equal, 1:greater than or equal, + 2:less than or equal) + default: 0 + title: Balance Operator + description: search condition of balance(0:equal, 1:greater than or equal, + 2:less than or equal) + - name: pending_transfer + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: number of pending transfer amount + title: Pending Transfer + description: number of pending transfer amount + - name: pending_transfer_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: search condition of pending transfer(0:equal, 1:greater than + or equal, 2:less than or equal) + default: 0 + title: Pending Transfer Operator + description: search condition of pending transfer(0:equal, 1:greater than + or equal, 2:less than or equal) + - name: locked + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: number of locked amount + title: Locked + description: number of locked amount + - name: locked_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: search condition of locked amount(0:equal, 1:greater than + or equal, 2:less than or equal) + default: 0 + title: Locked Operator + description: search condition of locked amount(0:equal, 1:greater than or + equal, 2:less than or equal) + - name: balance_and_pending_transfer + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: number of balance plus pending transfer amount + title: Balance And Pending Transfer + description: number of balance plus pending transfer amount + - name: balance_and_pending_transfer_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: search condition of balance plus pending transfer(0:equal, + 1:greater than or equal, 2:less than or equal) + default: 0 + title: Balance And Pending Transfer Operator + description: search condition of balance plus pending transfer(0:equal, + 1:greater than or equal, 2:less than or equal) + - name: account_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: account address(partial match) + title: Account Address + description: account address(partial match) + - name: holder_name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: holder name(partial match) + title: Holder Name + description: holder name(partial match) + - name: key_manager + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: key manager(partial match) + title: Key Manager + description: key manager(partial match) + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListAllHoldersSortItem' + description: Sort Item + default: created + title: Sort Item + description: Sort Item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 0 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HoldersResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /share/tokens/{token_address}/holders/count: + get: + tags: + - share + summary: Count Number Of Holders + description: Count the number of holders + operationId: count_number_of_holders_share_tokens__token_address__holders_count_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HolderCountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /share/tokens/{token_address}/holders/{account_address}: + get: + tags: + - share + summary: Retrieve Holder + description: Retrieve share token holder + operationId: retrieve_holder_share_tokens__token_address__holders__account_address__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HolderResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /share/tokens/{token_address}/personal_info: + post: + tags: + - share + summary: Register Holder Personal Info + description: Register the holder's personal information + operationId: register_holder_personal_info_share_tokens__token_address__personal_info_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterPersonalInfoRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Register Holder Personal Info Share Tokens Token + Address Personal Info Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Register Holder Personal Info Share Tokens Token + Address Personal Info Post + /share/tokens/{token_address}/personal_info/batch: + get: + tags: + - share + summary: List All Personal Info Batch Registration Uploads + description: List all personal information batch registration uploads + operationId: + list_all_personal_info_batch_registration_uploads_share_tokens__token_address__personal_info_batch_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: status + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Status + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListBatchRegisterPersonalInfoUploadResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - share + summary: Batch Register Personal Info + description: Create Batch for register personal information + operationId: + batch_register_personal_info_share_tokens__token_address__personal_info_batch_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RegisterPersonalInfoRequest' + title: Personal Info List + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BatchRegisterPersonalInfoUploadResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Batch Register Personal Info Share Tokens Token + Address Personal Info Batch Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /share/tokens/{token_address}/personal_info/batch/{batch_id}: + get: + tags: + - share + summary: Retrieve Batch Register Personal Info + description: Get Batch status for register personal information + operationId: + retrieve_batch_register_personal_info_share_tokens__token_address__personal_info_batch__batch_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: batch_id + in: path + required: true + schema: + type: string + title: Batch Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GetBatchRegisterPersonalInfoResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /share/tokens/{token_address}/lock_events: + get: + tags: + - share + summary: List all lock/unlock events related to given share token + operationId: list_all_lock_events_by_share_share_tokens__token_address__lock_events_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + - name: account_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Account address + title: Account Address + description: Account address + - name: msg_sender + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Msg sender + title: Msg Sender + description: Msg sender + - name: lock_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Lock address + title: Lock Address + description: Lock address + - name: recipient_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Recipient address + title: Recipient Address + description: Recipient address + - name: category + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/LockEventCategory' + - type: 'null' + description: Event category + title: Category + description: Event category + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListAllTokenLockEventsSortItem' + description: Sort item + default: block_timestamp + title: Sort Item + description: Sort item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 'Sort order(0: ASC, 1: DESC)' + default: 1 + title: Sort Order + description: 'Sort order(0: ASC, 1: DESC)' + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllTokenLockEventsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /share/transfers: + post: + tags: + - share + summary: Transfer Ownership + description: Transfer token ownership + operationId: transfer_ownership_share_transfers_post + parameters: + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetShareTransfer' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Transfer Ownership Share Transfers Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Transfer Ownership Share Transfers Post + /share/transfers/{token_address}: + get: + tags: + - share + summary: List Transfer History + description: List token transfer history + operationId: list_transfer_history_share_transfers__token_address__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: block_timestamp_from + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + description: block timestamp (From) + title: Block Timestamp From + description: block timestamp (From) + - name: block_timestamp_to + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + descriptio0n: block timestamp (To) + title: Block Timestamp To + - name: from_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: transfer source address + title: From Address + description: transfer source address + - name: to_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: transfer destination address + title: To Address + description: transfer destination address + - name: from_address_name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: name of transfer source address + title: From Address Name + description: name of transfer source address + - name: to_address_name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: name of transfer destination address + title: To Address Name + description: name of transfer destination address + - name: amount + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + description: transfer amount + title: Amount + description: transfer amount + - name: amount_operator + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ValueOperator' + - type: 'null' + description: 'value filter condition(0: equal, 1: greater than, 2: less + than)' + default: 0 + title: Amount Operator + description: 'value filter condition(0: equal, 1: greater than, 2: less + than)' + - name: source_event + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/TransferSourceEventType' + - type: 'null' + description: source event of transfer + title: Source Event + description: source event of transfer + - name: data + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: source event data + title: Data + description: source event data + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListTransferHistorySortItem' + description: sort item + default: block_timestamp + title: Sort Item + description: sort item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: start position + title: Offset + description: start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: number of set + title: Limit + description: number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /share/transfer_approvals: + get: + tags: + - share + summary: List Transfer Approval History + description: List transfer approval history + operationId: list_transfer_approval_history_share_transfer_approvals_get + parameters: + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Offset + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferApprovalsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /share/transfer_approvals/{token_address}: + get: + tags: + - share + summary: List Token Transfer Approval History + description: List token transfer approval history + operationId: + list_token_transfer_approval_history_share_transfer_approvals__token_address__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: from_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: From Address + - name: to_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: To Address + - name: status + in: query + required: false + schema: + anyOf: + - type: array + items: + $ref: '#/components/schemas/TransferApprovalStatus' + - type: 'null' + description: 0:unapproved, 1:escrow_finished, 2:transferred, 3:canceled + title: Status + description: 0:unapproved, 1:escrow_finished, 2:transferred, 3:canceled + - name: sort_item + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ListTransferApprovalHistorySortItem' + - type: 'null' + default: id + title: Sort Item + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: start position + title: Offset + description: start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: number of set + title: Limit + description: number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferApprovalHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /share/transfer_approvals/{token_address}/{id}: + post: + tags: + - share + summary: Update Transfer Approval + description: Update on the status of a share transfer approval + operationId: update_transfer_approval_share_transfer_approvals__token_address___id__post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: id + in: path + required: true + schema: + type: integer + title: Id + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateTransferApprovalRequest' + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Update Transfer Approval Share Transfer Approvals Token + Address Id Post + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/OperationNotAllowedStateErrorResponse' + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + - $ref: '#/components/schemas/ContractRevertErrorResponse' + title: Response 400 Update Transfer Approval Share Transfer Approvals Token + Address Id Post + get: + tags: + - share + summary: Retrieve Transfer Approval History + description: Retrieve share token transfer approval history + operationId: + retrieve_transfer_approval_history_share_transfer_approvals__token_address___id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: id + in: path + required: true + schema: + type: integer + title: Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TransferApprovalTokenDetailResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /share/bulk_transfer: + post: + tags: + - share + summary: Bulk Transfer Ownership + description: |- + Bulk transfer token ownership + + By using "transaction compression mode", it is possible to consolidate multiple transfers into one transaction. + This speeds up the time it takes for all transfers to be completed. + On the other hand, when using transaction compression, the input data must meet the following conditions. + - All `token_address` must be the same. + - All `from_address` must be the same. + - `from_address` and `issuer_address` must be the same. + operationId: bulk_transfer_ownership_share_bulk_transfer_post + parameters: + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IbetShareBulkTransferRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BulkTransferUploadIdResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '401': + description: Authorization Error + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/AuthorizationErrorResponse' + - $ref: '#/components/schemas/Error401Model' + title: Response 401 Bulk Transfer Ownership Share Bulk Transfer Post + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + get: + tags: + - share + summary: List Bulk Transfer Upload + description: List bulk transfer uploads + operationId: list_bulk_transfer_upload_share_bulk_transfer_get + parameters: + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BulkTransferUploadResponse' + title: Response List Bulk Transfer Upload Share Bulk Transfer Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /share/bulk_transfer/{upload_id}: + get: + tags: + - share + summary: Retrieve Bulk Transfer + description: Retrieve a bulk transfer upload + operationId: retrieve_bulk_transfer_share_bulk_transfer__upload_id__get + parameters: + - name: upload_id + in: path + required: true + schema: + type: string + title: Upload Id + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BulkTransferResponse' + title: Response Retrieve Bulk Transfer Share Bulk Transfer Upload + Id Get + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /token/holders/personal_info: + get: + tags: + - token_common + summary: List All Token Holders Personal Info + description: Lists the personal information of all registered holders linked + to the token issuer + operationId: ListTokenHoldersPersonalInfo + parameters: + - name: account_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: account address + title: Account Address + description: account address + - name: created_from + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: created datetime (From) + title: Created From + description: created datetime (From) + - name: created_to + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: created datetime (To) + title: Created To + description: created datetime (To) + - name: modified_from + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: modified datetime (From) + title: Modified From + description: modified datetime (From) + - name: modified_to + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: modified datetime (To) + title: Modified To + description: modified datetime (To) + - name: sort_item + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/ListTokenHoldersPersonalInfoSortItem' + description: sort item + default: created + title: Sort Item + description: sort item + - name: sort_order + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/SortOrder' + - type: 'null' + description: 'sort order(0: ASC, 1: DESC)' + default: 0 + title: Sort Order + description: 'sort order(0: ASC, 1: DESC)' + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: start position + title: Offset + description: start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: number of set + title: Limit + description: number of set + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListTokenHoldersPersonalInfoResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /token/holders/personal_info/history: + get: + tags: + - token_common + summary: List All Token Holders Personal Info History + description: List personal information historical data + operationId: ListTokenHoldersPersonalInfoHistory + parameters: + - name: account_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: account address + title: Account Address + description: account address + - name: event_type + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/PersonalInfoEventType' + - type: 'null' + description: event type + title: Event Type + description: event type + - name: block_timestamp_from + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: block timestamp datetime (From) + title: Block Timestamp From + description: block timestamp datetime (From) + - name: block_timestamp_to + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: block timestamp datetime (To) + title: Block Timestamp To + description: block timestamp datetime (To) + - name: created_from + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: created datetime (From) + title: Created From + description: created datetime (From) + - name: created_to + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: created datetime (To) + title: Created To + description: created datetime (To) + - name: sort_order + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/SortOrder' + - type: 'null' + description: 'sort order (0: ASC, 1: DESC)' + default: 0 + title: Sort Order + description: 'sort order (0: ASC, 1: DESC)' + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: start position + title: Offset + description: start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: number of set + title: Limit + description: number of set + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListTokenHoldersPersonalInfoHistoryResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + /token/holders/{token_address}/collection: + post: + tags: + - token_common + summary: Create Token Holders Collection + description: Create token holders collection + operationId: create_token_holders_collection_token_holders__token_address__collection_post + parameters: + - name: token_address + in: path + required: true + schema: + type: string + examples: + - '0xABCdeF1234567890abcdEf123456789000000000' + title: Token Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateTokenHoldersListRequest' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/CreateTokenHoldersListResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + get: + tags: + - token_common + summary: List All Token Holders Collections + operationId: list_all_token_holders_collections_token_holders__token_address__collection_get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: status + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/TokenHolderBatchStatus' + - type: 'null' + title: Status + - name: sort_order + in: query + required: false + schema: + type: integer + maximum: 1 + minimum: 0 + description: 0:asc, 1:desc (created) + default: 1 + title: Sort Order + description: 0:asc, 1:desc (created) + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Offset + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Limit + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllTokenHolderCollectionsResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /token/holders/{token_address}/collection/{list_id}: + get: + tags: + - token_common + summary: Retrieve Token Holders Collection + description: Retrieve token holders collection + operationId: + retrieve_token_holders_collection_token_holders__token_address__collection__list_id__get + parameters: + - name: token_address + in: path + required: true + schema: + type: string + title: Token Address + - name: list_id + in: path + required: true + schema: + type: string + description: UUID v4 required + examples: + - cfd83622-34dc-4efe-a68b-2cc275d3d824 + title: List Id + description: UUID v4 required + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/RetrieveTokenHoldersListResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /blockchain_explorer/block_data: + get: + tags: + - blockchain_explorer + summary: '[ibet Blockchain Explorer] List block data' + description: |- + Returns a list of block data within the specified block number range. + The maximum number of search results is 1000. + operationId: ListBlockData + parameters: + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: start position + title: Offset + description: start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: number of set + title: Limit + description: number of set + - name: from_block_number + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + title: From Block Number + - name: to_block_number + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + title: To Block Number + - name: sort_order + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/SortOrder' + - type: 'null' + description: 'sort order(0: ASC, 1: DESC)' + default: 0 + title: Sort Order + description: 'sort order(0: ASC, 1: DESC)' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BlockDataListResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/ResponseLimitExceededErrorResponse' + /blockchain_explorer/block_data/{block_number}: + get: + tags: + - blockchain_explorer + summary: '[ibet Blockchain Explorer] Retrieve block data' + description: Returns block data in the specified block number. + operationId: GetBlockData + parameters: + - name: block_number + in: path + required: true + schema: + type: integer + minimum: 0 + description: Block number + title: Block Number + description: Block number + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/BlockDataResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /blockchain_explorer/tx_data: + get: + tags: + - blockchain_explorer + summary: '[ibet Blockchain Explorer] List tx data' + description: |- + Returns a list of transactions by various search parameters. + The maximum number of search results is 10000. + operationId: ListTxData + parameters: + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: start position + title: Offset + description: start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: number of set + title: Limit + description: number of set + - name: block_number + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: block number + title: Block Number + description: block number + - name: from_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: tx from + title: From Address + description: tx from + - name: to_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: tx to + title: To Address + description: tx to + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TxDataListResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/ResponseLimitExceededErrorResponse' + /blockchain_explorer/tx_data/{hash}: + get: + tags: + - blockchain_explorer + summary: '[ibet Blockchain Explorer] Retrieve transaction data' + description: Searching for the transaction by transaction hash + operationId: GetTxData + parameters: + - name: hash + in: path + required: true + schema: + type: string + description: Transaction hash + title: Hash + description: Transaction hash + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/TxDataResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /freeze_log/accounts: + get: + tags: + - utility + summary: List All Accounts + description: List all freeze-logging accounts + operationId: ListAllFreezeLogAccount + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllFreezeLogAccountResponse' + post: + tags: + - utility + summary: Create Account + description: Create Freeze-Logging Account + operationId: CreateFreezeLogAccount + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFreezeLogAccountRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/FreezeLogAccountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /freeze_log/accounts/{account_address}: + delete: + tags: + - utility + summary: Delete Account + description: Logically delete an freeze-logging account + operationId: DeleteFreezeLogAccount + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/FreezeLogAccountResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /freeze_log/accounts/{account_address}/eoa_password: + post: + tags: + - utility + summary: Change Eoa Password + description: Change Account's EOA Password + operationId: ChangeFreezeLogAccountPassword + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/FreezeLogAccountChangeEOAPasswordRequest' + responses: + '200': + description: Successful Response + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /freeze_log/logs: + post: + tags: + - utility + summary: Record New Log + operationId: RecordNewFreezeLog + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RecordNewFreezeLogRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/RecordNewFreezeLogResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + title: Response 400 Recordnewfreezelog + /freeze_log/logs/{log_index}: + post: + tags: + - utility + summary: Update Log + operationId: UpdateFreezeLog + parameters: + - name: log_index + in: path + required: true + schema: + type: integer + description: Log index + title: Log Index + description: Log index + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateFreezeLogRequest' + responses: + '200': + description: Successful Response + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + title: Response 400 Updatefreezelog + get: + tags: + - utility + summary: Retrieve Log + operationId: RetrieveFreezeLog + parameters: + - name: log_index + in: path + required: true + schema: + type: integer + description: Log index + title: Log Index + description: Log index + - name: account_address + in: query + required: true + schema: + type: string + description: Logging account address + title: Account Address + description: Logging account address + responses: + '200': + description: Successful Response + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /settlement/dvp/agent/accounts: + get: + tags: + - token_common + summary: List All Accounts + description: List all DVP-Payment Agent accounts + operationId: ListAllDVPAgentAccount + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllDVPAgentAccountResponse' + post: + tags: + - token_common + summary: Create Account + description: Create DVP-Payment Agent Account + operationId: CreateDVPAgentAccount + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateDVPAgentAccountRequest' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/DVPAgentAccountResponse' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /settlement/dvp/agent/account/{account_address}: + delete: + tags: + - token_common + summary: Delete Account + description: Logically delete an DVP-Payment Agent Account + operationId: DeleteDVPAgentAccount + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/DVPAgentAccountResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + /settlement/dvp/agent/account/{account_address}/eoa_password: + post: + tags: + - token_common + summary: Change Eoa Password + description: Change Account's EOA Password + operationId: ChangeDVPAgentAccountPassword + parameters: + - name: account_address + in: path + required: true + schema: + type: string + title: Account Address + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DVPAgentAccountChangeEOAPasswordRequest' + responses: + '200': + description: Successful Response + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + /settlement/dvp/{exchange_address}/deliveries: + get: + tags: + - token_common + summary: List All Dvp Deliveries + description: List of DVP delivery + operationId: ListAllDVPDeliveries + parameters: + - name: exchange_address + in: path + required: true + schema: + type: string + title: Exchange Address + - name: token_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Token address + title: Token Address + description: Token address + - name: seller_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Seller address + title: Seller Address + description: Seller address + - name: buyer_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Buyer address + title: Buyer Address + description: Buyer address + - name: agent_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Agent address + title: Agent Address + description: Agent address + - name: valid + in: query + required: false + schema: + anyOf: + - type: boolean + - type: 'null' + description: Valid flag + title: Valid + description: Valid flag + - name: status + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/DeliveryStatus' + - type: 'null' + description: Delivery status + title: Status + description: Delivery status + - name: create_blocktimestamp_from + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + description: Create block timestamp filter(From) + title: Create Blocktimestamp From + description: Create block timestamp filter(From) + - name: create_blocktimestamp_to + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + description: Create block timestamp filter(To) + title: Create Blocktimestamp To + description: Create block timestamp filter(To) + - name: sort_order + in: query + required: false + schema: + allOf: + - $ref: '#/components/schemas/SortOrder' + description: 0:asc, 1:desc + default: 1 + title: Sort Order + description: 0:asc, 1:desc + - name: offset + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Start position + title: Offset + description: Start position + - name: limit + in: query + required: false + schema: + anyOf: + - type: integer + minimum: 0 + - type: 'null' + description: Number of set + title: Limit + description: Number of set + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/ListAllDVPDeliveriesResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - token_common + summary: Create Dvp Delivery + operationId: CreateDVPDelivery + parameters: + - name: exchange_address + in: path + required: true + schema: + type: string + title: Exchange Address + - name: issuer-address + in: header + required: true + schema: + type: string + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateDVPDeliveryRequest' + responses: + '200': + description: Successful Response + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + title: Response 400 Createdvpdelivery + /settlement/dvp/{exchange_address}/delivery/{delivery_id}: + get: + tags: + - token_common + summary: Retrieve Dvp Delivery + description: Retrieve a dvp delivery + operationId: RetrieveDVPDelivery + parameters: + - name: exchange_address + in: path + required: true + schema: + type: string + description: Exchange Address + title: Exchange Address + description: Exchange Address + - name: delivery_id + in: path + required: true + schema: + type: integer + description: Delivery Id + title: Delivery Id + description: Delivery Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/RetrieveDVPDeliveryResponse' + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidParameterErrorResponse' + post: + tags: + - token_common + summary: Update Dvp Delivery + operationId: UpdateDVPDelivery + parameters: + - name: exchange_address + in: path + required: true + schema: + type: string + title: Exchange Address + - name: delivery_id + in: path + required: true + schema: + type: string + title: Delivery Id + - name: issuer-address + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Issuer-Address + - name: eoa-password + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Eoa-Password + - name: auth-token + in: header + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Auth-Token + requestBody: + required: true + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/CancelDVPDeliveryRequest' + - $ref: '#/components/schemas/FinishDVPDeliveryRequest' + - $ref: '#/components/schemas/AbortDVPDeliveryRequest' + title: Data + responses: + '200': + description: Successful Response + '404': + description: Not Found Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error404Model' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error422Model' + '400': + description: Invalid Parameter Error / Send Transaction Error / Contract + Revert Error etc + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/SendTransactionErrorResponse' + - $ref: '#/components/schemas/InvalidParameterErrorResponse' + title: Response 400 Updatedvpdelivery +components: + schemas: + AbortDVPDeliveryRequest: + properties: + operation_type: + type: string + enum: + - Abort + const: Abort + title: Operation Type + account_address: + type: string + title: Account Address + description: Agent account address + eoa_password: + type: string + title: Eoa Password + description: Agent account key file password + type: object + required: + - operation_type + - account_address + - eoa_password + title: AbortDVPDeliveryRequest + description: DVP delivery abort schema (REQUEST) + AccountAuthTokenRequest: + properties: + valid_duration: + type: integer + maximum: 259200.0 + minimum: 0.0 + title: Valid Duration + type: object + title: AccountAuthTokenRequest + description: Account Create Auth Token schema (REQUEST) + AccountAuthTokenResponse: + properties: + auth_token: + type: string + title: Auth Token + usage_start: + type: string + format: date-time + title: Usage Start + valid_duration: + type: integer + title: Valid Duration + type: object + required: + - auth_token + - usage_start + - valid_duration + title: AccountAuthTokenResponse + description: Account Auth Token schema (RESPONSE) + AccountChangeEOAPasswordRequest: + properties: + old_eoa_password: + type: string + title: Old Eoa Password + eoa_password: + type: string + title: Eoa Password + type: object + required: + - old_eoa_password + - eoa_password + title: AccountChangeEOAPasswordRequest + description: Account Change EOA Password schema (REQUEST) + AccountChangeRSAPassphraseRequest: + properties: + old_rsa_passphrase: + type: string + title: Old Rsa Passphrase + rsa_passphrase: + type: string + title: Rsa Passphrase + type: object + required: + - old_rsa_passphrase + - rsa_passphrase + title: AccountChangeRSAPassphraseRequest + description: Account Change RSA Passphrase schema (REQUEST) + AccountCreateKeyRequest: + properties: + eoa_password: + type: string + title: Eoa Password + type: object + required: + - eoa_password + title: AccountCreateKeyRequest + description: Account Create Key schema (REQUEST) + AccountGenerateRsaKeyRequest: + properties: + rsa_passphrase: + anyOf: + - type: string + - type: 'null' + title: Rsa Passphrase + type: object + title: AccountGenerateRsaKeyRequest + description: Account Change Rsa Key schema (REQUEST) + AccountResponse: + properties: + issuer_address: + type: string + title: Issuer Address + rsa_public_key: + anyOf: + - type: string + - type: 'null' + title: Rsa Public Key + rsa_status: + $ref: '#/components/schemas/AccountRsaStatus' + is_deleted: + type: boolean + title: Is Deleted + type: object + required: + - issuer_address + - rsa_public_key + - rsa_status + - is_deleted + title: AccountResponse + description: Account schema (Response) + AccountRsaStatus: + type: integer + enum: + - 0 + - 1 + - 2 + - 3 + title: AccountRsaStatus + description: |- + 0:UNSET + 1:CREATING + 2:CHANGING + 3:SET + AuthTokenAlreadyExistsErrorCode: + type: integer + enum: + - 3 + const: 3 + title: AuthTokenAlreadyExistsErrorCode + AuthTokenAlreadyExistsErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/AuthTokenAlreadyExistsErrorCode' + examples: + - 3 + title: + type: string + title: Title + examples: + - AuthTokenAlreadyExistsError + type: object + required: + - code + - title + title: AuthTokenAlreadyExistsErrorMetainfo + AuthTokenAlreadyExistsErrorResponse: + properties: + meta: + $ref: '#/components/schemas/AuthTokenAlreadyExistsErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: AuthTokenAlreadyExistsErrorResponse + AuthorizationErrorCode: + type: integer + enum: + - 1 + const: 1 + title: AuthorizationErrorCode + AuthorizationErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/AuthorizationErrorCode' + examples: + - 1 + title: + type: string + title: Title + examples: + - AuthorizationError + type: object + required: + - code + - title + title: AuthorizationErrorMetainfo + AuthorizationErrorResponse: + properties: + meta: + $ref: '#/components/schemas/AuthorizationErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: AuthorizationErrorResponse + BatchIssueRedeemProcessedMetaInfo: + properties: + category: + $ref: '#/components/schemas/BatchIssueRedeemProcessingCategory' + upload_id: + type: string + title: Upload Id + error_data_id: + items: + type: integer + type: array + title: Error Data Id + token_address: + type: string + title: Token Address + token_type: + $ref: '#/components/schemas/TokenType' + type: object + required: + - category + - upload_id + - error_data_id + - token_address + - token_type + title: BatchIssueRedeemProcessedMetaInfo + BatchIssueRedeemProcessedNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 3.0 + minimum: 0.0 + title: Notice Code + description: |2- + - 0: All records successfully processed + - 1: Issuer does not exist + - 2: Failed to decode keyfile + - 3: Some records are failed to send transaction + created: + type: string + title: Created + notice_type: + type: string + enum: + - BatchIssueProcessed + const: BatchIssueProcessed + title: Notice Type + metainfo: + $ref: '#/components/schemas/BatchIssueRedeemProcessedMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: BatchIssueRedeemProcessedNotification + BatchIssueRedeemProcessingCategory: + type: string + enum: + - Issue + - Redeem + title: BatchIssueRedeemProcessingCategory + description: Batch Issue/Redeem Category + BatchIssueRedeemUpload: + properties: + batch_id: + type: string + title: Batch Id + description: UUID v4 required + issuer_address: + type: string + title: Issuer Address + token_type: + $ref: '#/components/schemas/TokenType' + token_address: + type: string + title: Token Address + processed: + type: boolean + title: Processed + created: + type: string + title: Created + type: object + required: + - batch_id + - issuer_address + - token_type + - token_address + - processed + - created + title: BatchIssueRedeemUpload + description: Batch issue/redeem Upload + examples: + - batch_id: cfd83622-34dc-4efe-a68b-2cc275d3d824 + created: '2022-09-02T19:49:33.370874+09:00' + issuer_address: '0x0000000000000000000000000000000000000000' + processed: true + token_address: '0x0000000000000000000000000000000000000000' + token_type: Bond + BatchIssueRedeemUploadIdResponse: + properties: + batch_id: + type: string + title: Batch Id + type: object + required: + - batch_id + title: BatchIssueRedeemUploadIdResponse + description: Batch issue/redeem upload id (RESPONSE) + BatchRegisterPersonalInfoErrorMetaInfo: + properties: + upload_id: + type: string + title: Upload Id + error_registration_id: + items: + type: integer + type: array + title: Error Registration Id + type: object + required: + - upload_id + - error_registration_id + title: BatchRegisterPersonalInfoErrorMetaInfo + BatchRegisterPersonalInfoErrorNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 1.0 + minimum: 0.0 + title: Notice Code + description: |2 + - 0: Issuer does not exist + - 1: Failed to send transaction + created: + type: string + title: Created + notice_type: + type: string + enum: + - BatchRegisterPersonalInfoError + const: BatchRegisterPersonalInfoError + title: Notice Type + metainfo: + $ref: '#/components/schemas/BatchRegisterPersonalInfoErrorMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: BatchRegisterPersonalInfoErrorNotification + BatchRegisterPersonalInfoResult: + properties: + status: + type: integer + title: Status + account_address: + type: string + title: Account Address + key_manager: + type: string + title: Key Manager + name: + anyOf: + - type: string + - type: 'null' + title: Name + postal_code: + anyOf: + - type: string + - type: 'null' + title: Postal Code + address: + anyOf: + - type: string + - type: 'null' + title: Address + email: + anyOf: + - type: string + - type: 'null' + title: Email + birth: + anyOf: + - type: string + - type: 'null' + title: Birth + is_corporate: + anyOf: + - type: boolean + - type: 'null' + title: Is Corporate + tax_category: + anyOf: + - type: integer + - type: 'null' + title: Tax Category + type: object + required: + - status + - account_address + - key_manager + - name + - postal_code + - address + - email + - birth + - is_corporate + - tax_category + title: BatchRegisterPersonalInfoResult + description: Result of Creating Batch Register PersonalInfo schema (RESPONSE) + BatchRegisterPersonalInfoUploadResponse: + properties: + batch_id: + type: string + title: Batch Id + description: UUID v4 required + status: + $ref: '#/components/schemas/BatchRegisterPersonalInfoUploadStatus' + created: + type: string + title: Created + type: object + required: + - batch_id + - status + - created + title: BatchRegisterPersonalInfoUploadResponse + description: Batch Register PersonalInfo schema (RESPONSE) + examples: + - batch_id: cfd83622-34dc-4efe-a68b-2cc275d3d824 + created: '2022-09-02T19:49:33.370874+09:00' + status: pending + BatchRegisterPersonalInfoUploadStatus: + type: string + enum: + - pending + - done + - failed + title: BatchRegisterPersonalInfoUploadStatus + description: Batch Register PersonalInfo Upload Status + BlockData: + properties: + number: + type: integer + minimum: 0.0 + title: Number + description: Block number + hash: + type: string + title: Hash + description: Block hash + transactions: + items: + type: string + type: array + title: Transactions + description: Transaction list + timestamp: + type: integer + title: Timestamp + gas_limit: + type: integer + title: Gas Limit + gas_used: + type: integer + title: Gas Used + size: + type: integer + minimum: 0.0 + title: Size + type: object + required: + - number + - hash + - transactions + - timestamp + - gas_limit + - gas_used + - size + title: BlockData + BlockDataDetail: + properties: + number: + type: integer + minimum: 0.0 + title: Number + description: Block number + parent_hash: + type: string + title: Parent Hash + sha3_uncles: + type: string + title: Sha3 Uncles + miner: + type: string + title: Miner + state_root: + type: string + title: State Root + transactions_root: + type: string + title: Transactions Root + receipts_root: + type: string + title: Receipts Root + logs_bloom: + type: string + title: Logs Bloom + difficulty: + type: integer + title: Difficulty + gas_limit: + type: integer + title: Gas Limit + gas_used: + type: integer + title: Gas Used + timestamp: + type: integer + title: Timestamp + proof_of_authority_data: + type: string + title: Proof Of Authority Data + mix_hash: + type: string + title: Mix Hash + nonce: + type: string + title: Nonce + hash: + type: string + title: Hash + description: Block hash + size: + type: integer + minimum: 0.0 + title: Size + transactions: + items: + type: string + type: array + title: Transactions + description: Transaction list + type: object + required: + - number + - parent_hash + - sha3_uncles + - miner + - state_root + - transactions_root + - receipts_root + - logs_bloom + - difficulty + - gas_limit + - gas_used + - timestamp + - proof_of_authority_data + - mix_hash + - nonce + - hash + - size + - transactions + title: BlockDataDetail + BlockDataListResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + block_data: + items: + $ref: '#/components/schemas/BlockData' + type: array + title: Block Data + type: object + required: + - result_set + - block_data + title: BlockDataListResponse + BlockDataResponse: + allOf: + - $ref: '#/components/schemas/BlockDataDetail' + title: BlockDataResponse + BlockNumberResponse: + properties: + block_number: + type: integer + title: Block Number + type: object + required: + - block_number + title: BlockNumberResponse + description: Block Number schema (Response) + BulkTransferErrorMetaInfo: + properties: + upload_id: + type: string + title: Upload Id + token_type: + $ref: '#/components/schemas/TokenType' + error_transfer_id: + items: + type: integer + type: array + title: Error Transfer Id + type: object + required: + - upload_id + - token_type + - error_transfer_id + title: BulkTransferErrorMetaInfo + BulkTransferErrorNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 2.0 + minimum: 0.0 + title: Notice Code + description: |2 + - 0: Issuer does not exist + - 1: Could not get the private key of the issuer + - 2: Failed to send transaction + created: + type: string + title: Created + notice_type: + type: string + enum: + - BulkTransferError + const: BulkTransferError + title: Notice Type + metainfo: + $ref: '#/components/schemas/BulkTransferErrorMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: BulkTransferErrorNotification + BulkTransferResponse: + properties: + upload_id: + type: string + title: Upload Id + description: Upload id + issuer_address: + type: string + title: Issuer Address + description: Issuer account address + token_address: + type: string + title: Token Address + description: Token address + token_type: + allOf: + - $ref: '#/components/schemas/TokenType' + description: Token type + from_address: + type: string + title: From Address + description: Transfer source address + to_address: + type: string + title: To Address + description: Transfer destination address + amount: + type: integer + title: Amount + description: Transfer amount + status: + type: integer + title: Status + description: Transfer status + transaction_error_code: + anyOf: + - type: integer + - type: 'null' + title: Transaction Error Code + description: Transfer error code + transaction_error_message: + anyOf: + - type: string + - type: 'null' + title: Transaction Error Message + description: Transfer error message + type: object + required: + - upload_id + - issuer_address + - token_address + - token_type + - from_address + - to_address + - amount + - status + - transaction_error_code + - transaction_error_message + title: BulkTransferResponse + description: bulk transfer data + BulkTransferUploadIdResponse: + properties: + upload_id: + type: string + title: Upload Id + description: Upload id + type: object + required: + - upload_id + title: BulkTransferUploadIdResponse + description: bulk transfer upload id + BulkTransferUploadResponse: + properties: + upload_id: + type: string + title: Upload Id + description: Upload id + issuer_address: + type: string + title: Issuer Address + description: Issuer account address + token_type: + allOf: + - $ref: '#/components/schemas/TokenType' + description: Token type + transaction_compression: + type: boolean + title: Transaction Compression + description: Transaction compression mode + status: + type: integer + title: Status + description: Processing status + created: + type: string + title: Created + description: Upload created datetime (ISO8601) + type: object + required: + - upload_id + - issuer_address + - token_type + - transaction_compression + - status + - created + title: BulkTransferUploadResponse + description: bulk transfer upload + CancelDVPDeliveryRequest: + properties: + operation_type: + type: string + enum: + - Cancel + const: Cancel + title: Operation Type + type: object + required: + - operation_type + title: CancelDVPDeliveryRequest + description: DVP delivery cancel schema (REQUEST) + ContractRevertErrorCode: + type: integer + enum: + - 0 + - 100001 + - 100002 + - 100101 + - 100102 + - 110001 + - 110002 + - 110101 + - 110102 + - 110201 + - 110202 + - 110301 + - 110401 + - 110402 + - 110501 + - 110502 + - 110503 + - 110504 + - 110601 + - 110701 + - 110702 + - 110801 + - 110802 + - 110901 + - 110902 + - 111001 + - 111002 + - 111101 + - 111102 + - 111201 + - 120001 + - 120002 + - 120101 + - 120102 + - 120201 + - 120202 + - 120301 + - 120401 + - 120402 + - 120501 + - 120502 + - 120503 + - 120601 + - 120701 + - 120702 + - 120801 + - 120802 + - 120901 + - 120902 + - 121001 + - 121002 + - 121101 + - 121102 + - 121201 + - 130001 + - 130101 + - 130102 + - 130201 + - 130202 + - 130203 + - 130301 + - 130401 + - 130501 + - 130502 + - 140001 + - 140101 + - 140102 + - 140201 + - 140202 + - 140203 + - 140301 + - 140401 + - 140402 + - 150001 + - 150101 + - 150201 + - 150202 + - 150301 + - 200001 + - 210001 + - 210101 + - 210102 + - 210103 + - 210104 + - 210201 + - 210202 + - 210203 + - 210204 + - 210301 + - 210302 + - 210401 + - 210402 + - 210403 + - 210501 + - 210502 + - 210503 + - 210504 + - 210601 + - 220001 + - 230001 + - 230002 + - 230003 + - 230101 + - 230102 + - 230103 + - 230104 + - 230201 + - 230202 + - 230203 + - 230204 + - 230301 + - 240001 + - 240002 + - 240003 + - 240101 + - 240102 + - 240103 + - 240104 + - 240201 + - 240202 + - 240203 + - 240204 + - 240205 + - 240301 + - 240302 + - 240303 + - 240304 + - 240401 + - 250001 + - 260001 + - 260002 + - 260003 + - 260004 + - 260101 + - 260102 + - 260103 + - 260104 + - 260201 + - 260202 + - 260203 + - 260204 + - 260205 + - 260206 + - 260301 + - 260302 + - 260303 + - 260304 + - 260305 + - 260306 + - 260401 + - 260402 + - 260403 + - 260404 + - 260501 + - 300001 + - 300101 + - 300201 + - 300301 + - 300401 + - 300501 + - 400001 + - 400002 + - 500001 + - 500101 + - 600001 + - 600002 + - 610001 + - 610011 + - 620001 + title: ContractRevertErrorCode + ContractRevertErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/ContractRevertErrorCode' + examples: + - 0 + title: + type: string + title: Title + examples: + - ContractRevertError + type: object + required: + - code + - title + title: ContractRevertErrorMetainfo + ContractRevertErrorResponse: + properties: + meta: + $ref: '#/components/schemas/ContractRevertErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: ContractRevertErrorResponse + description: |- + Revert error occurs from smart-contract + + - Error code: https://github.com/BoostryJP/ibet-SmartContract/blob/master/docs/Errors.md + - If contract doesn't throw error code, 0 is returned. + CreateDVPAgentAccountRequest: + properties: + eoa_password: + type: string + title: Eoa Password + description: EOA keyfile password + type: object + required: + - eoa_password + title: CreateDVPAgentAccountRequest + description: DVP agent account create schema (REQUEST) + CreateDVPDeliveryRequest: + properties: + token_address: + type: string + title: Token Address + buyer_address: + type: string + title: Buyer Address + amount: + type: integer + maximum: 1000000000000.0 + minimum: 1.0 + title: Amount + agent_address: + type: string + title: Agent Address + data: + type: string + title: Data + type: object + required: + - token_address + - buyer_address + - amount + - agent_address + - data + title: CreateDVPDeliveryRequest + description: DVP delivery create schema (REQUEST) + CreateFreezeLogAccountRequest: + properties: + eoa_password: + type: string + title: Eoa Password + description: EOA keyfile password + type: object + required: + - eoa_password + title: CreateFreezeLogAccountRequest + description: Freeze-Logging account create schema (REQUEST) + CreateLedgerInfoMetaInfo: + properties: + token_address: + type: string + title: Token Address + token_type: + $ref: '#/components/schemas/TokenType' + ledger_id: + type: integer + title: Ledger Id + type: object + required: + - token_address + - token_type + - ledger_id + title: CreateLedgerInfoMetaInfo + CreateLedgerInfoNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 0.0 + minimum: 0.0 + title: Notice Code + description: " - 0: Created ledger info successfully\n" + created: + type: string + title: Created + notice_type: + type: string + enum: + - CreateLedgerInfo + const: CreateLedgerInfo + title: Notice Type + metainfo: + $ref: '#/components/schemas/CreateLedgerInfoMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: CreateLedgerInfoNotification + CreateTokenHoldersListRequest: + properties: + list_id: + type: string + title: List Id + description: UUID v4 required + block_number: + type: integer + minimum: 1.0 + title: Block Number + type: object + required: + - list_id + - block_number + title: CreateTokenHoldersListRequest + description: Create Token Holders List schema (REQUEST) + examples: + - block_number: 765 + list_id: cfd83622-34dc-4efe-a68b-2cc275d3d824 + CreateTokenHoldersListResponse: + properties: + list_id: + type: string + title: List Id + description: UUID v4 required + status: + $ref: '#/components/schemas/TokenHolderBatchStatus' + type: object + required: + - list_id + - status + title: CreateTokenHoldersListResponse + description: Create Token Holders List schema (RESPONSE) + examples: + - list_id: cfd83622-34dc-4efe-a68b-2cc275d3d824 + status: pending + CreateUpdateLedgerDetailsDataRequest: + properties: + name: + anyOf: + - type: string + maxLength: 200 + - type: 'null' + title: Name + address: + anyOf: + - type: string + maxLength: 200 + - type: 'null' + title: Address + amount: + anyOf: + - type: integer + maximum: 1000000000000.0 + minimum: 0.0 + - type: 'null' + title: Amount + price: + anyOf: + - type: integer + maximum: 1000000000000.0 + minimum: 0.0 + - type: 'null' + title: Price + balance: + anyOf: + - type: integer + maximum: 9.223372036854776e+18 + minimum: 0.0 + - type: 'null' + title: Balance + acquisition_date: + anyOf: + - type: string + maxLength: 10 + minLength: 10 + - type: 'null' + title: Acquisition Date + description: YYYY/MM/DD + type: object + title: CreateUpdateLedgerDetailsDataRequest + description: Create or Update Ledger Details Data Structure schema (Request) + CreateUpdateLedgerDetailsDataTemplateRequest: + properties: + type: + $ref: '#/components/schemas/LedgerDetailsDataType' + source: + anyOf: + - type: string + maxLength: 42 + - type: 'null' + title: Source + type: object + required: + - type + title: CreateUpdateLedgerDetailsDataTemplateRequest + description: Create or Update Ledger Details Data Template schema (Request) + CreateUpdateLedgerDetailsTemplateRequest: + properties: + token_detail_type: + type: string + maxLength: 100 + title: Token Detail Type + headers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Headers + data: + $ref: '#/components/schemas/CreateUpdateLedgerDetailsDataTemplateRequest' + footers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Footers + type: object + required: + - token_detail_type + - data + title: CreateUpdateLedgerDetailsTemplateRequest + description: Create or Update Ledger Details Template schema (Request) + CreateUpdateLedgerTemplateRequest: + properties: + token_name: + type: string + maxLength: 200 + title: Token Name + headers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Headers + details: + items: + $ref: '#/components/schemas/CreateUpdateLedgerDetailsTemplateRequest' + type: array + title: Details + footers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Footers + type: object + required: + - token_name + - details + title: CreateUpdateLedgerTemplateRequest + description: Create or Update Ledger Template schema (Request) + DVPAgentAccountChangeEOAPasswordRequest: + properties: + old_eoa_password: + type: string + title: Old Eoa Password + description: EOA keyfile password (old) + eoa_password: + type: string + title: Eoa Password + description: EOA keyfile password (new) + type: object + required: + - old_eoa_password + - eoa_password + title: DVPAgentAccountChangeEOAPasswordRequest + description: DVP agent account change EOA password schema (REQUEST) + DVPAgentAccountResponse: + properties: + account_address: + type: string + title: Account Address + is_deleted: + type: boolean + title: Is Deleted + type: object + required: + - account_address + - is_deleted + title: DVPAgentAccountResponse + description: DVP agent account reference schema (RESPONSE) + DeliveryStatus: + type: integer + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + title: DeliveryStatus + description: DVP Delivery Status + DownloadFileResponse: + properties: + file_id: + type: string + title: File Id + issuer_address: + type: string + title: Issuer Address + relation: + anyOf: + - type: string + - type: 'null' + title: Relation + file_name: + type: string + title: File Name + content: + type: string + title: Content + description: Base64-encoded content + content_size: + type: integer + title: Content Size + description: + anyOf: + - type: string + - type: 'null' + title: Description + label: + type: string + title: Label + type: object + required: + - file_id + - issuer_address + - relation + - file_name + - content + - content_size + - description + - label + title: DownloadFileResponse + description: Download File schema (Response) + E2EEResponse: + properties: + public_key: + anyOf: + - type: string + - type: 'null' + title: Public Key + type: object + required: + - public_key + title: E2EEResponse + description: E2EE schema (Response) + E2EMessagingAccountChangeEOAPasswordRequest: + properties: + old_eoa_password: + type: string + title: Old Eoa Password + eoa_password: + type: string + title: Eoa Password + type: object + required: + - old_eoa_password + - eoa_password + title: E2EMessagingAccountChangeEOAPasswordRequest + description: E2E Messaging Account Change EOA Password schema (REQUEST) + E2EMessagingAccountChangeRSAPassphraseRequest: + properties: + old_rsa_passphrase: + type: string + title: Old Rsa Passphrase + rsa_passphrase: + type: string + title: Rsa Passphrase + type: object + required: + - old_rsa_passphrase + - rsa_passphrase + title: E2EMessagingAccountChangeRSAPassphraseRequest + description: E2E Messaging Account Change RSA Passphrase schema (REQUEST) + E2EMessagingAccountCreateRequest: + properties: + eoa_password: + type: string + title: Eoa Password + rsa_passphrase: + anyOf: + - type: string + - type: 'null' + title: Rsa Passphrase + rsa_key_generate_interval: + anyOf: + - type: integer + maximum: 10000.0 + minimum: 0.0 + - type: 'null' + title: Rsa Key Generate Interval + description: 0 disables auto-generate(Unit is hour) + default: 24 + rsa_generation: + anyOf: + - type: integer + maximum: 100.0 + minimum: 0.0 + - type: 'null' + title: Rsa Generation + description: 0 disables generation + default: 7 + type: object + required: + - eoa_password + title: E2EMessagingAccountCreateRequest + description: E2E Messaging Account Create schema (REQUEST) + E2EMessagingAccountResponse: + properties: + account_address: + type: string + title: Account Address + rsa_key_generate_interval: + anyOf: + - type: integer + - type: 'null' + title: Rsa Key Generate Interval + rsa_generation: + anyOf: + - type: integer + - type: 'null' + title: Rsa Generation + rsa_public_key: + anyOf: + - type: string + - type: 'null' + title: Rsa Public Key + is_deleted: + type: boolean + title: Is Deleted + type: object + required: + - account_address + - rsa_key_generate_interval + - rsa_generation + - rsa_public_key + - is_deleted + title: E2EMessagingAccountResponse + description: E2E Messaging Account schema (Response) + E2EMessagingAccountUpdateRsaKeyRequest: + properties: + rsa_key_generate_interval: + anyOf: + - type: integer + maximum: 10000.0 + minimum: 0.0 + - type: 'null' + title: Rsa Key Generate Interval + description: 0 disables auto-generate(Unit is hour) + default: 24 + rsa_generation: + anyOf: + - type: integer + maximum: 100.0 + minimum: 0.0 + - type: 'null' + title: Rsa Generation + description: 0 disables generation + default: 7 + type: object + title: E2EMessagingAccountUpdateRsaKeyRequest + description: E2E Messaging Account Rsa Key Update schema (REQUEST) + E2EMessagingResponse: + properties: + id: + type: integer + title: Id + from_address: + type: string + title: From Address + to_address: + type: string + title: To Address + type: + type: string + title: Type + message: + anyOf: + - type: string + - type: object + - items: {} + type: array + title: Message + send_timestamp: + type: string + format: date-time + title: Send Timestamp + type: object + required: + - id + - from_address + - to_address + - type + - message + - send_timestamp + title: E2EMessagingResponse + description: E2E Messaging schema (Response) + Error401MetaModel: + properties: + code: + type: integer + title: Code + examples: + - 1 + title: + type: string + title: Title + examples: + - AuthorizationError + type: object + required: + - code + - title + title: Error401MetaModel + Error401Model: + properties: + meta: + $ref: '#/components/schemas/Error401MetaModel' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: Error401Model + Error404MetaModel: + properties: + code: + type: integer + title: Code + examples: + - 1 + title: + type: string + title: Title + examples: + - NotFound + type: object + required: + - code + - title + title: Error404MetaModel + Error404Model: + properties: + meta: + $ref: '#/components/schemas/Error404MetaModel' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: Error404Model + Error422DetailModel: + properties: + loc: + items: + type: string + type: array + title: Loc + examples: + - - header + - issuer-address + msg: + type: string + title: Msg + examples: + - field required + type: + type: string + title: Type + examples: + - value_error.missing + type: object + required: + - loc + - msg + - type + title: Error422DetailModel + Error422MetaModel: + properties: + code: + type: integer + title: Code + examples: + - 1 + title: + type: string + title: Title + examples: + - RequestValidationError + type: object + required: + - code + - title + title: Error422MetaModel + Error422Model: + properties: + meta: + $ref: '#/components/schemas/Error422MetaModel' + detail: + items: + $ref: '#/components/schemas/Error422DetailModel' + type: array + title: Detail + type: object + required: + - meta + - detail + title: Error422Model + FileResponse: + properties: + file_id: + type: string + title: File Id + issuer_address: + type: string + title: Issuer Address + relation: + anyOf: + - type: string + - type: 'null' + title: Relation + file_name: + type: string + title: File Name + content_size: + type: integer + title: Content Size + description: + anyOf: + - type: string + - type: 'null' + title: Description + label: + type: string + title: Label + created: + type: string + format: date-time + title: Created + type: object + required: + - file_id + - issuer_address + - relation + - file_name + - content_size + - description + - label + - created + title: FileResponse + description: File schema (Response) + FinishDVPDeliveryRequest: + properties: + operation_type: + type: string + enum: + - Finish + const: Finish + title: Operation Type + account_address: + type: string + title: Account Address + description: Agent account address + eoa_password: + type: string + title: Eoa Password + description: Agent account key file password + type: object + required: + - operation_type + - account_address + - eoa_password + title: FinishDVPDeliveryRequest + description: DVP delivery finish schema (REQUEST) + ForceUnlockRequest: + properties: + token_address: + type: string + title: Token Address + description: Token address + lock_address: + type: string + title: Lock Address + description: Lock address + recipient_address: + type: string + title: Recipient Address + description: Recipient address + value: + type: integer + exclusiveMinimum: 0.0 + title: Value + description: Unlock amount + type: object + required: + - token_address + - lock_address + - recipient_address + - value + title: ForceUnlockRequest + FreezeLogAccountChangeEOAPasswordRequest: + properties: + old_eoa_password: + type: string + title: Old Eoa Password + description: EOA keyfile password (old) + eoa_password: + type: string + title: Eoa Password + description: EOA keyfile password (new) + type: object + required: + - old_eoa_password + - eoa_password + title: FreezeLogAccountChangeEOAPasswordRequest + description: Freeze-Logging account change EOA password schema (REQUEST) + FreezeLogAccountResponse: + properties: + account_address: + type: string + title: Account Address + is_deleted: + type: boolean + title: Is Deleted + type: object + required: + - account_address + - is_deleted + title: FreezeLogAccountResponse + description: Freeze-logging account reference schema (RESPONSE) + GetBatchIssueRedeemResponse: + properties: + processed: + type: boolean + title: Processed + results: + items: + $ref: '#/components/schemas/GetBatchIssueRedeemResult' + type: array + title: Results + type: object + required: + - processed + - results + title: GetBatchIssueRedeemResponse + description: Get Batch issue/redeem upload schema (RESPONSE) + GetBatchIssueRedeemResult: + properties: + account_address: + type: string + title: Account Address + amount: + type: integer + title: Amount + status: + type: integer + title: Status + personal_information: + $ref: '#/components/schemas/PersonalInfo' + type: object + required: + - account_address + - amount + - status + - personal_information + title: GetBatchIssueRedeemResult + description: Result of Creating Batch issue/redeem schema (RESPONSE) + GetBatchRegisterPersonalInfoResponse: + properties: + status: + $ref: '#/components/schemas/BatchRegisterPersonalInfoUploadStatus' + results: + items: + $ref: '#/components/schemas/BatchRegisterPersonalInfoResult' + type: array + title: Results + type: object + required: + - status + - results + title: GetBatchRegisterPersonalInfoResponse + description: Get Batch Register PersonalInfo schema (RESPONSE) + HTTPValidationError: + properties: + detail: + items: + $ref: '#/components/schemas/ValidationError' + type: array + title: Detail + type: object + title: HTTPValidationError + HolderCountResponse: + properties: + count: + type: integer + title: Count + type: object + required: + - count + title: HolderCountResponse + description: Holder count schema (Response) + HolderResponse: + properties: + account_address: + type: string + title: Account Address + personal_information: + $ref: '#/components/schemas/PersonalInfo' + balance: + type: integer + title: Balance + exchange_balance: + type: integer + title: Exchange Balance + exchange_commitment: + type: integer + title: Exchange Commitment + pending_transfer: + type: integer + title: Pending Transfer + locked: + type: integer + title: Locked + modified: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Modified + type: object + required: + - account_address + - personal_information + - balance + - exchange_balance + - exchange_commitment + - pending_transfer + - locked + - modified + title: HolderResponse + description: Holder schema (Response) + HoldersResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + holders: + items: + $ref: '#/components/schemas/HolderResponse' + type: array + title: Holders + type: object + required: + - result_set + - holders + title: HoldersResponse + description: Holders schema (Response) + IbetShareAdditionalIssue: + properties: + account_address: + type: string + title: Account Address + amount: + type: integer + maximum: 1000000000000.0 + minimum: 1.0 + title: Amount + type: object + required: + - account_address + - amount + title: IbetShareAdditionalIssue + description: ibet Share schema (Additional Issue) + IbetShareBulkTransferRequest: + properties: + transfer_list: + items: + $ref: '#/components/schemas/IbetShareTransfer' + type: array + maxItems: 500000 + minItems: 1 + title: Transfer List + description: List of data to be transferred + transaction_compression: + anyOf: + - type: boolean + - type: 'null' + title: Transaction Compression + description: Transaction compression mode + type: object + required: + - transfer_list + title: IbetShareBulkTransferRequest + IbetShareContractVersion: + type: string + enum: + - '22_12' + - '24_06' + title: IbetShareContractVersion + IbetShareCreate: + properties: + name: + type: string + maxLength: 100 + title: Name + issue_price: + type: integer + maximum: 5000000000.0 + minimum: 0.0 + title: Issue Price + principal_value: + type: integer + maximum: 5000000000.0 + minimum: 0.0 + title: Principal Value + total_supply: + type: integer + maximum: 1000000000000.0 + minimum: 0.0 + title: Total Supply + symbol: + anyOf: + - type: string + maxLength: 100 + - type: 'null' + title: Symbol + dividends: + anyOf: + - type: number + maximum: 5000000000.0 + minimum: 0.0 + - type: 'null' + title: Dividends + dividend_record_date: + anyOf: + - type: string + pattern: ^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + - type: string + enum: + - '' + const: '' + - type: 'null' + title: Dividend Record Date + dividend_payment_date: + anyOf: + - type: string + pattern: ^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + - type: string + enum: + - '' + const: '' + - type: 'null' + title: Dividend Payment Date + cancellation_date: + anyOf: + - type: string + pattern: ^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + - type: string + enum: + - '' + const: '' + - type: 'null' + title: Cancellation Date + transferable: + anyOf: + - type: boolean + - type: 'null' + title: Transferable + status: + anyOf: + - type: boolean + - type: 'null' + title: Status + is_offering: + anyOf: + - type: boolean + - type: 'null' + title: Is Offering + tradable_exchange_contract_address: + anyOf: + - type: string + - type: 'null' + title: Tradable Exchange Contract Address + personal_info_contract_address: + anyOf: + - type: string + - type: 'null' + title: Personal Info Contract Address + require_personal_info_registered: + anyOf: + - type: boolean + - type: 'null' + title: Require Personal Info Registered + contact_information: + anyOf: + - type: string + maxLength: 2000 + - type: 'null' + title: Contact Information + privacy_policy: + anyOf: + - type: string + maxLength: 5000 + - type: 'null' + title: Privacy Policy + transfer_approval_required: + anyOf: + - type: boolean + - type: 'null' + title: Transfer Approval Required + is_canceled: + anyOf: + - type: boolean + - type: 'null' + title: Is Canceled + type: object + required: + - name + - issue_price + - principal_value + - total_supply + title: IbetShareCreate + description: ibet Share schema (Create) + IbetShareRedeem: + properties: + account_address: + type: string + title: Account Address + amount: + type: integer + maximum: 1000000000000.0 + minimum: 1.0 + title: Amount + type: object + required: + - account_address + - amount + title: IbetShareRedeem + description: ibet Share schema (Redeem) + IbetShareResponse: + properties: + issuer_address: + type: string + title: Issuer Address + token_address: + type: string + title: Token Address + name: + type: string + title: Name + symbol: + type: string + title: Symbol + issue_price: + type: integer + title: Issue Price + principal_value: + type: integer + title: Principal Value + total_supply: + type: integer + title: Total Supply + dividends: + type: number + title: Dividends + dividend_record_date: + type: string + title: Dividend Record Date + dividend_payment_date: + type: string + title: Dividend Payment Date + cancellation_date: + type: string + title: Cancellation Date + transferable: + type: boolean + title: Transferable + transfer_approval_required: + type: boolean + title: Transfer Approval Required + status: + type: boolean + title: Status + is_offering: + type: boolean + title: Is Offering + tradable_exchange_contract_address: + type: string + title: Tradable Exchange Contract Address + personal_info_contract_address: + type: string + title: Personal Info Contract Address + require_personal_info_registered: + type: boolean + title: Require Personal Info Registered + contact_information: + type: string + title: Contact Information + privacy_policy: + type: string + title: Privacy Policy + issue_datetime: + type: string + title: Issue Datetime + token_status: + type: integer + title: Token Status + is_canceled: + type: boolean + title: Is Canceled + memo: + type: string + title: Memo + contract_version: + $ref: '#/components/schemas/IbetShareContractVersion' + type: object + required: + - issuer_address + - token_address + - name + - symbol + - issue_price + - principal_value + - total_supply + - dividends + - dividend_record_date + - dividend_payment_date + - cancellation_date + - transferable + - transfer_approval_required + - status + - is_offering + - tradable_exchange_contract_address + - personal_info_contract_address + - require_personal_info_registered + - contact_information + - privacy_policy + - issue_datetime + - token_status + - is_canceled + - memo + - contract_version + title: IbetShareResponse + description: ibet Share schema (Response) + IbetShareScheduledUpdate: + properties: + scheduled_datetime: + type: string + format: date-time + title: Scheduled Datetime + event_type: + $ref: '#/components/schemas/ScheduledEventType' + data: + $ref: '#/components/schemas/IbetShareUpdate' + type: object + required: + - scheduled_datetime + - event_type + - data + title: IbetShareScheduledUpdate + description: scheduled event (Request) + IbetShareTransfer: + properties: + token_address: + type: string + title: Token Address + from_address: + type: string + title: From Address + to_address: + type: string + title: To Address + amount: + type: integer + maximum: 1000000000000.0 + minimum: 1.0 + title: Amount + type: object + required: + - token_address + - from_address + - to_address + - amount + title: IbetShareTransfer + description: ibet Share schema (Transfer) + IbetShareUpdate: + properties: + cancellation_date: + anyOf: + - type: string + pattern: ^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + - type: string + enum: + - '' + const: '' + - type: 'null' + title: Cancellation Date + dividend_record_date: + anyOf: + - type: string + pattern: ^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + - type: string + enum: + - '' + const: '' + - type: 'null' + title: Dividend Record Date + dividend_payment_date: + anyOf: + - type: string + pattern: ^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + - type: string + enum: + - '' + const: '' + - type: 'null' + title: Dividend Payment Date + dividends: + anyOf: + - type: number + maximum: 5000000000.0 + minimum: 0.0 + - type: 'null' + title: Dividends + tradable_exchange_contract_address: + anyOf: + - type: string + - type: 'null' + title: Tradable Exchange Contract Address + personal_info_contract_address: + anyOf: + - type: string + - type: 'null' + title: Personal Info Contract Address + require_personal_info_registered: + anyOf: + - type: boolean + - type: 'null' + title: Require Personal Info Registered + transferable: + anyOf: + - type: boolean + - type: 'null' + title: Transferable + status: + anyOf: + - type: boolean + - type: 'null' + title: Status + is_offering: + anyOf: + - type: boolean + - type: 'null' + title: Is Offering + contact_information: + anyOf: + - type: string + maxLength: 2000 + - type: 'null' + title: Contact Information + privacy_policy: + anyOf: + - type: string + maxLength: 5000 + - type: 'null' + title: Privacy Policy + transfer_approval_required: + anyOf: + - type: boolean + - type: 'null' + title: Transfer Approval Required + principal_value: + anyOf: + - type: integer + maximum: 5000000000.0 + minimum: 0.0 + - type: 'null' + title: Principal Value + is_canceled: + anyOf: + - type: boolean + - type: 'null' + title: Is Canceled + memo: + anyOf: + - type: string + maxLength: 10000 + - type: 'null' + title: Memo + type: object + title: IbetShareUpdate + description: ibet Share schema (Update) + IbetStraightBondAdditionalIssue: + properties: + account_address: + type: string + title: Account Address + amount: + type: integer + maximum: 1000000000000.0 + minimum: 1.0 + title: Amount + type: object + required: + - account_address + - amount + title: IbetStraightBondAdditionalIssue + description: ibet Straight Bond schema (Additional Issue) + IbetStraightBondBulkTransferRequest: + properties: + transfer_list: + items: + $ref: '#/components/schemas/IbetStraightBondTransfer' + type: array + maxItems: 500000 + minItems: 1 + title: Transfer List + description: List of data to be transferred + transaction_compression: + anyOf: + - type: boolean + - type: 'null' + title: Transaction Compression + description: Transaction compression mode + type: object + required: + - transfer_list + title: IbetStraightBondBulkTransferRequest + IbetStraightBondContractVersion: + type: string + enum: + - '22_12' + - '23_12' + - '24_06' + title: IbetStraightBondContractVersion + IbetStraightBondCreate: + properties: + name: + type: string + maxLength: 100 + title: Name + total_supply: + type: integer + maximum: 1000000000000.0 + minimum: 0.0 + title: Total Supply + face_value: + type: integer + maximum: 5000000000.0 + minimum: 0.0 + title: Face Value + face_value_currency: + type: string + maxLength: 3 + minLength: 3 + title: Face Value Currency + purpose: + type: string + maxLength: 2000 + title: Purpose + symbol: + anyOf: + - type: string + maxLength: 100 + - type: 'null' + title: Symbol + redemption_date: + anyOf: + - type: string + pattern: ^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + - type: 'null' + title: Redemption Date + redemption_value: + anyOf: + - type: integer + maximum: 5000000000.0 + minimum: 0.0 + - type: 'null' + title: Redemption Value + redemption_value_currency: + anyOf: + - type: string + maxLength: 3 + minLength: 3 + - type: 'null' + title: Redemption Value Currency + return_date: + anyOf: + - type: string + pattern: ^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + - type: 'null' + title: Return Date + return_amount: + anyOf: + - type: string + maxLength: 2000 + - type: 'null' + title: Return Amount + interest_rate: + anyOf: + - type: number + maximum: 100.0 + minimum: 0.0 + - type: 'null' + title: Interest Rate + interest_payment_date: + anyOf: + - items: + type: string + pattern: ^(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + type: array + - type: 'null' + title: Interest Payment Date + interest_payment_currency: + anyOf: + - type: string + maxLength: 3 + minLength: 3 + - type: 'null' + title: Interest Payment Currency + base_fx_rate: + anyOf: + - type: number + minimum: 0.0 + - type: 'null' + title: Base Fx Rate + transferable: + anyOf: + - type: boolean + - type: 'null' + title: Transferable + is_redeemed: + anyOf: + - type: boolean + - type: 'null' + title: Is Redeemed + status: + anyOf: + - type: boolean + - type: 'null' + title: Status + is_offering: + anyOf: + - type: boolean + - type: 'null' + title: Is Offering + tradable_exchange_contract_address: + anyOf: + - type: string + - type: 'null' + title: Tradable Exchange Contract Address + personal_info_contract_address: + anyOf: + - type: string + - type: 'null' + title: Personal Info Contract Address + require_personal_info_registered: + anyOf: + - type: boolean + - type: 'null' + title: Require Personal Info Registered + image_url: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Image Url + contact_information: + anyOf: + - type: string + maxLength: 2000 + - type: 'null' + title: Contact Information + privacy_policy: + anyOf: + - type: string + maxLength: 5000 + - type: 'null' + title: Privacy Policy + transfer_approval_required: + anyOf: + - type: boolean + - type: 'null' + title: Transfer Approval Required + type: object + required: + - name + - total_supply + - face_value + - face_value_currency + - purpose + title: IbetStraightBondCreate + description: ibet Straight Bond schema (Create) + IbetStraightBondRedeem: + properties: + account_address: + type: string + title: Account Address + amount: + type: integer + maximum: 1000000000000.0 + minimum: 1.0 + title: Amount + type: object + required: + - account_address + - amount + title: IbetStraightBondRedeem + description: ibet Straight Bond schema (Redeem) + IbetStraightBondResponse: + properties: + issuer_address: + type: string + title: Issuer Address + token_address: + type: string + title: Token Address + name: + type: string + title: Name + symbol: + type: string + title: Symbol + total_supply: + type: integer + title: Total Supply + face_value: + type: integer + title: Face Value + face_value_currency: + type: string + title: Face Value Currency + redemption_date: + type: string + title: Redemption Date + redemption_value: + type: integer + title: Redemption Value + redemption_value_currency: + type: string + title: Redemption Value Currency + return_date: + type: string + title: Return Date + return_amount: + type: string + title: Return Amount + purpose: + type: string + title: Purpose + interest_rate: + type: number + title: Interest Rate + interest_payment_date: + items: + type: string + type: array + title: Interest Payment Date + interest_payment_currency: + type: string + title: Interest Payment Currency + base_fx_rate: + type: number + title: Base Fx Rate + transferable: + type: boolean + title: Transferable + is_redeemed: + type: boolean + title: Is Redeemed + status: + type: boolean + title: Status + is_offering: + type: boolean + title: Is Offering + tradable_exchange_contract_address: + type: string + title: Tradable Exchange Contract Address + personal_info_contract_address: + type: string + title: Personal Info Contract Address + require_personal_info_registered: + type: boolean + title: Require Personal Info Registered + contact_information: + type: string + title: Contact Information + privacy_policy: + type: string + title: Privacy Policy + issue_datetime: + type: string + title: Issue Datetime + token_status: + type: integer + title: Token Status + transfer_approval_required: + type: boolean + title: Transfer Approval Required + memo: + type: string + title: Memo + contract_version: + $ref: '#/components/schemas/IbetStraightBondContractVersion' + type: object + required: + - issuer_address + - token_address + - name + - symbol + - total_supply + - face_value + - face_value_currency + - redemption_date + - redemption_value + - redemption_value_currency + - return_date + - return_amount + - purpose + - interest_rate + - interest_payment_date + - interest_payment_currency + - base_fx_rate + - transferable + - is_redeemed + - status + - is_offering + - tradable_exchange_contract_address + - personal_info_contract_address + - require_personal_info_registered + - contact_information + - privacy_policy + - issue_datetime + - token_status + - transfer_approval_required + - memo + - contract_version + title: IbetStraightBondResponse + description: ibet Straight Bond schema (Response) + IbetStraightBondScheduledUpdate: + properties: + scheduled_datetime: + type: string + format: date-time + title: Scheduled Datetime + event_type: + $ref: '#/components/schemas/ScheduledEventType' + data: + $ref: '#/components/schemas/IbetStraightBondUpdate' + type: object + required: + - scheduled_datetime + - event_type + - data + title: IbetStraightBondScheduledUpdate + description: scheduled event (Request) + IbetStraightBondTransfer: + properties: + token_address: + type: string + title: Token Address + from_address: + type: string + title: From Address + to_address: + type: string + title: To Address + amount: + type: integer + maximum: 1000000000000.0 + minimum: 1.0 + title: Amount + type: object + required: + - token_address + - from_address + - to_address + - amount + title: IbetStraightBondTransfer + description: ibet Straight Bond schema (Transfer) + IbetStraightBondUpdate: + properties: + face_value: + anyOf: + - type: integer + maximum: 5000000000.0 + minimum: 0.0 + - type: 'null' + title: Face Value + face_value_currency: + anyOf: + - type: string + maxLength: 3 + minLength: 3 + - type: 'null' + title: Face Value Currency + interest_rate: + anyOf: + - type: number + maximum: 100.0 + minimum: 0.0 + - type: 'null' + title: Interest Rate + interest_payment_date: + anyOf: + - items: + type: string + pattern: ^(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$ + type: array + - type: 'null' + title: Interest Payment Date + interest_payment_currency: + anyOf: + - type: string + maxLength: 3 + minLength: 3 + - type: string + enum: + - '' + const: '' + - type: 'null' + title: Interest Payment Currency + redemption_value: + anyOf: + - type: integer + maximum: 5000000000.0 + minimum: 0.0 + - type: 'null' + title: Redemption Value + redemption_value_currency: + anyOf: + - type: string + maxLength: 3 + minLength: 3 + - type: string + enum: + - '' + const: '' + - type: 'null' + title: Redemption Value Currency + base_fx_rate: + anyOf: + - type: number + minimum: 0.0 + - type: 'null' + title: Base Fx Rate + transferable: + anyOf: + - type: boolean + - type: 'null' + title: Transferable + status: + anyOf: + - type: boolean + - type: 'null' + title: Status + is_offering: + anyOf: + - type: boolean + - type: 'null' + title: Is Offering + is_redeemed: + anyOf: + - type: boolean + - type: 'null' + title: Is Redeemed + tradable_exchange_contract_address: + anyOf: + - type: string + - type: 'null' + title: Tradable Exchange Contract Address + personal_info_contract_address: + anyOf: + - type: string + - type: 'null' + title: Personal Info Contract Address + require_personal_info_registered: + anyOf: + - type: boolean + - type: 'null' + title: Require Personal Info Registered + contact_information: + anyOf: + - type: string + maxLength: 2000 + - type: 'null' + title: Contact Information + privacy_policy: + anyOf: + - type: string + maxLength: 5000 + - type: 'null' + title: Privacy Policy + transfer_approval_required: + anyOf: + - type: boolean + - type: 'null' + title: Transfer Approval Required + memo: + anyOf: + - type: string + maxLength: 10000 + - type: 'null' + title: Memo + type: object + title: IbetStraightBondUpdate + description: ibet Straight Bond schema (Update) + Integer64bitLimitExceededErrorCode: + type: integer + enum: + - 5 + const: 5 + title: Integer64bitLimitExceededErrorCode + Integer64bitLimitExceededErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/Integer64bitLimitExceededErrorCode' + examples: + - 5 + title: + type: string + title: Title + examples: + - Integer64bitLimitExceededError + type: object + required: + - code + - title + title: Integer64bitLimitExceededErrorMetainfo + Integer64bitLimitExceededErrorResponse: + properties: + meta: + $ref: '#/components/schemas/Integer64bitLimitExceededErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: Integer64bitLimitExceededErrorResponse + InvalidParameterErrorCode: + type: integer + enum: + - 1 + const: 1 + title: InvalidParameterErrorCode + InvalidParameterErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/InvalidParameterErrorCode' + examples: + - 1 + title: + type: string + title: Title + examples: + - InvalidParameterError + type: object + required: + - code + - title + title: InvalidParameterErrorMetainfo + InvalidParameterErrorResponse: + properties: + meta: + $ref: '#/components/schemas/InvalidParameterErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: InvalidParameterErrorResponse + IssueErrorMetaInfo: + properties: + token_address: + type: string + title: Token Address + token_type: + $ref: '#/components/schemas/TokenType' + arguments: + type: object + title: Arguments + type: object + required: + - token_address + - token_type + - arguments + title: IssueErrorMetaInfo + IssueErrorNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 2.0 + minimum: 0.0 + title: Notice Code + description: |2 + - 0: Issuer does not exist + - 1: Could not get the private key of the issuer + - 2: Failed to send transaction + created: + type: string + title: Created + notice_type: + type: string + enum: + - IssueError + const: IssueError + title: Notice Type + metainfo: + $ref: '#/components/schemas/IssueErrorMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: IssueErrorNotification + IssueRedeemEvent: + properties: + transaction_hash: + type: string + title: Transaction Hash + token_address: + type: string + title: Token Address + locked_address: + type: string + title: Locked Address + target_address: + type: string + title: Target Address + amount: + type: integer + title: Amount + block_timestamp: + type: string + title: Block Timestamp + type: object + required: + - transaction_hash + - token_address + - locked_address + - target_address + - amount + - block_timestamp + title: IssueRedeemEvent + description: Issue/Redeem event + IssueRedeemHistoryResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + history: + items: + $ref: '#/components/schemas/IssueRedeemEvent' + type: array + title: History + type: object + required: + - result_set + - history + title: IssueRedeemHistoryResponse + description: Issue/Redeem history + IssueRedeemSortItem: + type: string + enum: + - block_timestamp + - locked_address + - target_address + - amount + title: IssueRedeemSortItem + description: Issue/Redeem sort item + LedgerDetailsDataListAllResponse: + properties: + data_id: + type: string + title: Data Id + count: + type: integer + title: Count + created: + type: string + format: date-time + title: Created + type: object + required: + - data_id + - count + - created + title: LedgerDetailsDataListAllResponse + description: Ledger Details Data(List All) schema (Response) + LedgerDetailsDataResponse: + properties: + data_id: + type: string + title: Data Id + type: object + required: + - data_id + title: LedgerDetailsDataResponse + description: Ledger Details Data schema (Response) + LedgerDetailsDataTemplateResponse: + properties: + type: + $ref: '#/components/schemas/LedgerDetailsDataType' + source: + anyOf: + - type: string + - type: 'null' + title: Source + type: object + required: + - type + - source + title: LedgerDetailsDataTemplateResponse + description: Ledger Details Data Template schema (Response) + LedgerDetailsDataType: + type: string + enum: + - ibetfin + - db + title: LedgerDetailsDataType + LedgerDetailsTemplateResponse: + properties: + token_detail_type: + type: string + title: Token Detail Type + headers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Headers + data: + $ref: '#/components/schemas/LedgerDetailsDataTemplateResponse' + footers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Footers + type: object + required: + - token_detail_type + - headers + - data + - footers + title: LedgerDetailsTemplateResponse + description: Ledger Details Template schema (Response) + LedgerResponse: + properties: + id: + type: integer + title: Id + token_address: + type: string + title: Token Address + token_type: + $ref: '#/components/schemas/TokenType' + created: + type: string + format: date-time + title: Created + type: object + required: + - id + - token_address + - token_type + - created + title: LedgerResponse + description: Ledger schema (Response) + LedgerTemplateResponse: + properties: + token_name: + type: string + title: Token Name + headers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Headers + details: + items: + $ref: '#/components/schemas/LedgerDetailsTemplateResponse' + type: array + title: Details + footers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Footers + type: object + required: + - token_name + - headers + - details + - footers + title: LedgerTemplateResponse + description: Ledger Template schema (Response) + ListAllDVPAgentAccountResponse: + items: + $ref: '#/components/schemas/DVPAgentAccountResponse' + type: array + title: ListAllDVPAgentAccountResponse + description: DVP agent account list reference schema (RESPONSE) + ListAllDVPDeliveriesResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + deliveries: + items: + $ref: '#/components/schemas/RetrieveDVPDeliveryResponse' + type: array + title: Deliveries + type: object + required: + - result_set + - deliveries + title: ListAllDVPDeliveriesResponse + description: List all DVP delivery schema (Response) + ListAllE2EMessagingResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + e2e_messages: + items: + $ref: '#/components/schemas/E2EMessagingResponse' + type: array + title: E2E Messages + type: object + required: + - result_set + - e2e_messages + title: ListAllE2EMessagingResponse + description: List All E2E Messaging schema (Response) + ListAllFilesResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + files: + items: + $ref: '#/components/schemas/FileResponse' + type: array + title: Files + type: object + required: + - result_set + - files + title: ListAllFilesResponse + description: List All Files schema (Response) + ListAllFreezeLogAccountResponse: + items: + $ref: '#/components/schemas/FreezeLogAccountResponse' + type: array + title: ListAllFreezeLogAccountResponse + description: Freeze-logging account list reference schema (RESPONSE) + ListAllHoldersSortItem: + type: string + enum: + - created + - account_address + - balance + - pending_transfer + - locked + - balance_and_pending_transfer + - key_manager + - holder_name + title: ListAllHoldersSortItem + ListAllLedgerDetailsDataResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + details_data: + items: + $ref: '#/components/schemas/LedgerDetailsDataListAllResponse' + type: array + title: Details Data + type: object + required: + - result_set + - details_data + title: ListAllLedgerDetailsDataResponse + description: List All Ledger Details Data schema (Response) + ListAllLedgerHistoryResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + ledgers: + items: + $ref: '#/components/schemas/LedgerResponse' + type: array + title: Ledgers + type: object + required: + - result_set + - ledgers + title: ListAllLedgerHistoryResponse + description: List All Ledger History schema (Response) + ListAllLockEventsResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + events: + items: + $ref: '#/components/schemas/LockEvent' + type: array + title: Events + description: Lock/Unlock event list + type: object + required: + - result_set + - events + title: ListAllLockEventsResponse + description: List All Lock/Unlock events (Response) + ListAllLockEventsSortItem: + type: string + enum: + - token_address + - lock_address + - recipient_address + - value + - block_timestamp + title: ListAllLockEventsSortItem + ListAllLockedPositionResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + locked_positions: + items: + $ref: '#/components/schemas/LockedPosition' + type: array + title: Locked Positions + description: Locked position list + type: object + required: + - result_set + - locked_positions + title: ListAllLockedPositionResponse + description: List All Locked Position schema (Response) + ListAllNotificationsResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + notifications: + items: + $ref: '#/components/schemas/NotificationsListResponse' + type: array + title: Notifications + type: object + required: + - result_set + - notifications + title: ListAllNotificationsResponse + description: List All Notifications schema (Response) + ListAllPositionResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + positions: + items: + $ref: '#/components/schemas/Position' + type: array + title: Positions + description: Position list + type: object + required: + - result_set + - positions + title: ListAllPositionResponse + description: List All Position schema (Response) + ListAllTokenHolderCollectionsResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + collections: + items: + $ref: '#/components/schemas/RetrieveTokenHolderCollectionResponse' + type: array + title: Collections + type: object + required: + - result_set + - collections + title: ListAllTokenHolderCollectionsResponse + description: List All Token Holders Collections schema (RESPONSE) + ListAllTokenLockEventsResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + events: + items: + $ref: '#/components/schemas/LockEvent' + type: array + title: Events + description: Lock/Unlock event list + type: object + required: + - result_set + - events + title: ListAllTokenLockEventsResponse + description: List All Lock/Unlock events (Response) + ListAllTokenLockEventsSortItem: + type: string + enum: + - account_address + - lock_address + - recipient_address + - value + - block_timestamp + title: ListAllTokenLockEventsSortItem + ListBatchIssueRedeemUploadResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + uploads: + items: + $ref: '#/components/schemas/BatchIssueRedeemUpload' + type: array + title: Uploads + type: object + required: + - result_set + - uploads + title: ListBatchIssueRedeemUploadResponse + description: List All Batch issue/redeem Upload(RESPONSE) + ListBatchRegisterPersonalInfoUploadResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + uploads: + items: + $ref: '#/components/schemas/BatchRegisterPersonalInfoUploadResponse' + type: array + title: Uploads + type: object + required: + - result_set + - uploads + title: ListBatchRegisterPersonalInfoUploadResponse + description: List All Batch Register PersonalInfo Upload (Response) + ListTokenHistorySortItem: + type: string + enum: + - created + - operation_category + title: ListTokenHistorySortItem + description: Sort item of token history + ListTokenHoldersPersonalInfoHistoryResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + personal_info: + items: + $ref: '#/components/schemas/PersonalInfoHistory' + type: array + title: Personal Info + type: object + required: + - result_set + - personal_info + title: ListTokenHoldersPersonalInfoHistoryResponse + description: List All Token Holders PersonalInfo Histories (Response) + ListTokenHoldersPersonalInfoResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + personal_info: + items: + $ref: '#/components/schemas/PersonalInfoIndex' + type: array + title: Personal Info + type: object + required: + - result_set + - personal_info + title: ListTokenHoldersPersonalInfoResponse + description: List All Token Holders PersonalInfo (Response) + ListTokenHoldersPersonalInfoSortItem: + type: string + enum: + - account_address + - created + - modified + title: ListTokenHoldersPersonalInfoSortItem + ListTokenOperationLogHistoryResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + history: + items: + $ref: '#/components/schemas/TokenOperationLogResponse' + type: array + title: History + description: token update histories + default: [] + type: object + required: + - result_set + title: ListTokenOperationLogHistoryResponse + ListTransferApprovalHistorySortItem: + type: string + enum: + - id + - exchange_address + - application_id + - from_address + - to_address + - amount + - application_datetime + - approval_datetime + - status + title: ListTransferApprovalHistorySortItem + ListTransferHistorySortItem: + type: string + enum: + - block_timestamp + - from_address + - to_address + - from_address_name + - to_address_name + - amount + title: ListTransferHistorySortItem + LockEvent: + properties: + category: + allOf: + - $ref: '#/components/schemas/LockEventCategory' + description: Event category + transaction_hash: + type: string + title: Transaction Hash + description: Transaction hash + msg_sender: + anyOf: + - type: string + - type: 'null' + title: Msg Sender + description: Message sender + issuer_address: + type: string + title: Issuer Address + description: Issuer address + token_address: + type: string + title: Token Address + description: Token address + token_type: + allOf: + - $ref: '#/components/schemas/TokenType' + description: Token type + token_name: + type: string + title: Token Name + description: Token name + lock_address: + type: string + title: Lock Address + description: Lock address + account_address: + type: string + title: Account Address + description: Account address + recipient_address: + anyOf: + - type: string + - type: 'null' + title: Recipient Address + description: Recipient address + value: + type: integer + title: Value + description: Lock/Unlock amount + data: + type: object + title: Data + description: Message at lock/unlock + block_timestamp: + type: string + title: Block Timestamp + description: block_timestamp when Lock log was emitted + type: object + required: + - category + - transaction_hash + - issuer_address + - token_address + - token_type + - token_name + - lock_address + - account_address + - value + - data + - block_timestamp + title: LockEvent + LockEventCategory: + type: string + enum: + - Lock + - Unlock + title: LockEventCategory + LockInfoMetaInfo: + properties: + token_address: + type: string + title: Token Address + token_type: + $ref: '#/components/schemas/TokenType' + lock_address: + type: string + title: Lock Address + account_address: + type: string + title: Account Address + value: + type: integer + title: Value + data: + type: object + title: Data + type: object + required: + - token_address + - token_type + - lock_address + - account_address + - value + - data + title: LockInfoMetaInfo + LockInfoNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 0.0 + minimum: 0.0 + title: Notice Code + description: " - 0: Balance is locked\n" + created: + type: string + title: Created + notice_type: + type: string + enum: + - LockInfo + const: LockInfo + title: Notice Type + metainfo: + $ref: '#/components/schemas/LockInfoMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: LockInfoNotification + LockedPosition: + properties: + issuer_address: + type: string + title: Issuer Address + description: Issuer address + token_address: + type: string + title: Token Address + description: Token address + token_type: + allOf: + - $ref: '#/components/schemas/TokenType' + description: Token type + token_name: + type: string + title: Token Name + description: Token name + lock_address: + type: string + title: Lock Address + description: Lock address + locked: + type: integer + title: Locked + description: Locked amount + type: object + required: + - issuer_address + - token_address + - token_type + - token_name + - lock_address + - locked + title: LockedPosition + description: Locked Position + NotificationsListResponse: + anyOf: + - $ref: '#/components/schemas/IssueErrorNotification' + - $ref: '#/components/schemas/BulkTransferErrorNotification' + - $ref: '#/components/schemas/ScheduleEventErrorNotification' + - $ref: '#/components/schemas/TransferApprovalInfoNotification' + - $ref: '#/components/schemas/CreateLedgerInfoNotification' + - $ref: '#/components/schemas/BatchRegisterPersonalInfoErrorNotification' + - $ref: '#/components/schemas/BatchIssueRedeemProcessedNotification' + - $ref: '#/components/schemas/LockInfoNotification' + - $ref: '#/components/schemas/UnlockInfoNotification' + title: NotificationsListResponse + description: Notifications List schema (Response) + OperationNotAllowedStateErrorCode: + type: integer + enum: + - 101 + const: 101 + title: OperationNotAllowedStateErrorCode + OperationNotAllowedStateErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/OperationNotAllowedStateErrorCode' + examples: + - 0 + title: + type: string + title: Title + examples: + - OperationNotAllowedStateError + type: object + required: + - code + - title + title: OperationNotAllowedStateErrorMetainfo + OperationNotAllowedStateErrorResponse: + properties: + meta: + $ref: '#/components/schemas/OperationNotAllowedStateErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: OperationNotAllowedStateErrorResponse + description: Error returned when server-side data is not ready to process the + request + OperationNotSupportedVersionErrorCode: + type: integer + enum: + - 6 + const: 6 + title: OperationNotSupportedVersionErrorCode + OperationNotSupportedVersionErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/OperationNotSupportedVersionErrorCode' + examples: + - 6 + title: + type: string + title: Title + examples: + - OperationNotSupportedVersionError + type: object + required: + - code + - title + title: OperationNotSupportedVersionErrorMetainfo + OperationNotSupportedVersionErrorResponse: + properties: + meta: + $ref: '#/components/schemas/OperationNotSupportedVersionErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: OperationNotSupportedVersionErrorResponse + description: The token version for which the operation is not supported + PersonalInfo: + properties: + key_manager: + anyOf: + - type: string + - type: 'null' + title: Key Manager + name: + anyOf: + - type: string + - type: 'null' + title: Name + postal_code: + anyOf: + - type: string + - type: 'null' + title: Postal Code + address: + anyOf: + - type: string + - type: 'null' + title: Address + email: + anyOf: + - type: string + - type: 'null' + title: Email + birth: + anyOf: + - type: string + - type: 'null' + title: Birth + is_corporate: + anyOf: + - type: boolean + - type: 'null' + title: Is Corporate + tax_category: + anyOf: + - type: integer + - type: 'null' + title: Tax Category + type: object + required: + - key_manager + - name + - postal_code + - address + - email + - birth + - is_corporate + - tax_category + title: PersonalInfo + PersonalInfoEventType: + type: string + enum: + - register + - modify + title: PersonalInfoEventType + PersonalInfoHistory: + properties: + id: + type: integer + title: Id + account_address: + type: string + title: Account Address + event_type: + $ref: '#/components/schemas/PersonalInfoEventType' + personal_info: + $ref: '#/components/schemas/PersonalInfo' + block_timestamp: + type: string + format: date-time + title: Block Timestamp + created: + type: string + format: date-time + title: Created + type: object + required: + - id + - account_address + - event_type + - personal_info + - block_timestamp + - created + title: PersonalInfoHistory + description: Personal Information History schema + PersonalInfoIndex: + properties: + id: + type: integer + title: Id + account_address: + type: string + title: Account Address + personal_info: + $ref: '#/components/schemas/PersonalInfo' + created: + type: string + format: date-time + title: Created + modified: + type: string + format: date-time + title: Modified + type: object + required: + - id + - account_address + - personal_info + - created + - modified + title: PersonalInfoIndex + description: Personal Information Index schema + Position: + properties: + issuer_address: + type: string + title: Issuer Address + description: Issuer address + token_address: + type: string + title: Token Address + description: Token address + token_type: + allOf: + - $ref: '#/components/schemas/TokenType' + description: Token type + token_name: + type: string + title: Token Name + description: Token name + balance: + type: integer + title: Balance + description: Balance + exchange_balance: + type: integer + title: Exchange Balance + description: Balance on the exchange contract + exchange_commitment: + type: integer + title: Exchange Commitment + description: Commitment on the exchange contract + pending_transfer: + type: integer + title: Pending Transfer + description: Pending transfer amount + locked: + type: integer + title: Locked + description: Total locked amount + type: object + required: + - issuer_address + - token_address + - token_type + - token_name + - balance + - exchange_balance + - exchange_commitment + - pending_transfer + - locked + title: Position + description: Position + PositionResponse: + allOf: + - $ref: '#/components/schemas/Position' + title: PositionResponse + description: Position schema (Response) + RecordNewFreezeLogRequest: + properties: + account_address: + type: string + title: Account Address + description: Logging account address + eoa_password: + type: string + title: Eoa Password + description: Logging account key file password + log_message: + type: string + title: Log Message + description: Log message + freezing_grace_block_count: + type: integer + exclusiveMinimum: 0.0 + title: Freezing Grace Block Count + description: Freezing grace block count + type: object + required: + - account_address + - eoa_password + - log_message + - freezing_grace_block_count + title: RecordNewFreezeLogRequest + description: Record new freeze log schema (REQUEST) + RecordNewFreezeLogResponse: + properties: + log_index: + type: integer + title: Log Index + type: object + required: + - log_index + title: RecordNewFreezeLogResponse + description: New freeze-log recording schema (RESPONSE) + RegisterPersonalInfoRequest: + properties: + name: + anyOf: + - type: string + - type: 'null' + title: Name + postal_code: + anyOf: + - type: string + - type: 'null' + title: Postal Code + address: + anyOf: + - type: string + - type: 'null' + title: Address + email: + anyOf: + - type: string + - type: 'null' + title: Email + birth: + anyOf: + - type: string + - type: 'null' + title: Birth + is_corporate: + anyOf: + - type: boolean + - type: 'null' + title: Is Corporate + tax_category: + anyOf: + - type: integer + - type: 'null' + title: Tax Category + account_address: + type: string + title: Account Address + key_manager: + type: string + title: Key Manager + type: object + required: + - account_address + - key_manager + title: RegisterPersonalInfoRequest + description: Register Personal Information schema (REQUEST) + ResponseLimitExceededErrorCode: + type: integer + enum: + - 4 + const: 4 + title: ResponseLimitExceededErrorCode + ResponseLimitExceededErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/ResponseLimitExceededErrorCode' + examples: + - 4 + title: + type: string + title: Title + examples: + - ResponseLimitExceededError + type: object + required: + - code + - title + title: ResponseLimitExceededErrorMetainfo + ResponseLimitExceededErrorResponse: + properties: + meta: + $ref: '#/components/schemas/ResponseLimitExceededErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: ResponseLimitExceededErrorResponse + ResultSet: + properties: + count: + anyOf: + - type: integer + - type: 'null' + title: Count + offset: + anyOf: + - type: integer + - type: 'null' + title: Offset + limit: + anyOf: + - type: integer + - type: 'null' + title: Limit + total: + anyOf: + - type: integer + - type: 'null' + title: Total + type: object + required: + - count + - offset + - limit + - total + title: ResultSet + description: result set for pagination + RetrieveDVPDeliveryResponse: + properties: + exchange_address: + type: string + title: Exchange Address + delivery_id: + type: integer + title: Delivery Id + token_address: + type: string + title: Token Address + buyer_address: + type: string + title: Buyer Address + seller_address: + type: string + title: Seller Address + amount: + type: integer + title: Amount + agent_address: + type: string + title: Agent Address + data: + type: string + title: Data + examples: + - '{}' + - '{"type": "primary"}' + create_blocktimestamp: + type: string + title: Create Blocktimestamp + create_transaction_hash: + type: string + title: Create Transaction Hash + cancel_blocktimestamp: + anyOf: + - type: string + - type: 'null' + title: Cancel Blocktimestamp + cancel_transaction_hash: + anyOf: + - type: string + - type: 'null' + title: Cancel Transaction Hash + confirm_blocktimestamp: + anyOf: + - type: string + - type: 'null' + title: Confirm Blocktimestamp + confirm_transaction_hash: + anyOf: + - type: string + - type: 'null' + title: Confirm Transaction Hash + finish_blocktimestamp: + anyOf: + - type: string + - type: 'null' + title: Finish Blocktimestamp + finish_transaction_hash: + anyOf: + - type: string + - type: 'null' + title: Finish Transaction Hash + abort_blocktimestamp: + anyOf: + - type: string + - type: 'null' + title: Abort Blocktimestamp + abort_transaction_hash: + anyOf: + - type: string + - type: 'null' + title: Abort Transaction Hash + confirmed: + type: boolean + title: Confirmed + valid: + type: boolean + title: Valid + status: + $ref: '#/components/schemas/DeliveryStatus' + type: object + required: + - exchange_address + - delivery_id + - token_address + - buyer_address + - seller_address + - amount + - agent_address + - data + - create_blocktimestamp + - create_transaction_hash + - cancel_blocktimestamp + - cancel_transaction_hash + - confirm_blocktimestamp + - confirm_transaction_hash + - finish_blocktimestamp + - finish_transaction_hash + - abort_blocktimestamp + - abort_transaction_hash + - confirmed + - valid + - status + title: RetrieveDVPDeliveryResponse + description: Retrieve DVP delivery schema (Response) + RetrieveLedgerDetailsDataHistoryResponse: + properties: + account_address: + anyOf: + - type: string + - type: 'null' + title: Account Address + name: + anyOf: + - type: string + - type: 'null' + title: Name + address: + anyOf: + - type: string + - type: 'null' + title: Address + amount: + type: integer + title: Amount + price: + type: integer + title: Price + balance: + type: integer + title: Balance + acquisition_date: + type: string + title: Acquisition Date + type: object + required: + - amount + - price + - balance + - acquisition_date + title: RetrieveLedgerDetailsDataHistoryResponse + description: Retrieve Ledger Details Data History schema (Response) + RetrieveLedgerDetailsDataResponse: + properties: + name: + anyOf: + - type: string + - type: 'null' + title: Name + address: + anyOf: + - type: string + - type: 'null' + title: Address + amount: + type: integer + title: Amount + price: + type: integer + title: Price + balance: + type: integer + title: Balance + acquisition_date: + type: string + title: Acquisition Date + type: object + required: + - name + - address + - amount + - price + - balance + - acquisition_date + title: RetrieveLedgerDetailsDataResponse + description: Retrieve Ledger Details Data schema (Response) + RetrieveLedgerDetailsHistoryResponse: + properties: + token_detail_type: + type: string + title: Token Detail Type + headers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Headers + data: + items: + $ref: '#/components/schemas/RetrieveLedgerDetailsDataHistoryResponse' + type: array + title: Data + footers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Footers + some_personal_info_not_registered: + type: boolean + title: Some Personal Info Not Registered + type: object + required: + - token_detail_type + - data + - some_personal_info_not_registered + title: RetrieveLedgerDetailsHistoryResponse + description: Retrieve Ledger Details History schema (Response) + RetrieveLedgerHistoryResponse: + properties: + created: + type: string + title: Created + token_name: + type: string + title: Token Name + currency: + type: string + title: Currency + headers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Headers + details: + items: + $ref: '#/components/schemas/RetrieveLedgerDetailsHistoryResponse' + type: array + title: Details + footers: + anyOf: + - items: + type: object + type: array + - type: 'null' + title: Footers + type: object + required: + - created + - token_name + - currency + - details + title: RetrieveLedgerHistoryResponse + description: Retrieve Ledger History schema (Response) + RetrieveTokenHolderCollectionResponse: + properties: + token_address: + type: string + title: Token Address + block_number: + type: integer + title: Block Number + list_id: + type: string + title: List Id + description: UUID v4 required + status: + $ref: '#/components/schemas/TokenHolderBatchStatus' + type: object + required: + - token_address + - block_number + - list_id + - status + title: RetrieveTokenHolderCollectionResponse + description: Retrieve Token Holders Collection schema (RESPONSE) + RetrieveTokenHoldersListResponse: + properties: + status: + $ref: '#/components/schemas/TokenHolderBatchStatus' + holders: + items: + $ref: '#/components/schemas/TokenHoldersCollectionHolder' + type: array + title: Holders + type: object + required: + - status + - holders + title: RetrieveTokenHoldersListResponse + description: Retrieve Token Holders List schema (RESPONSE) + examples: + - holders: + - account_address: '0x85a8b8887a4bD76859751b10C8aC8EC5f3aA1bDB' + hold_balance: 30000 + locked_balance: 0 + status: done + ScheduleEventErrorMetaInfo: + properties: + scheduled_event_id: + type: string + title: Scheduled Event Id + token_address: + anyOf: + - type: string + - type: 'null' + title: Token Address + token_type: + $ref: '#/components/schemas/TokenType' + type: object + required: + - scheduled_event_id + - token_type + title: ScheduleEventErrorMetaInfo + ScheduleEventErrorNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 2.0 + minimum: 0.0 + title: Notice Code + description: |2 + - 0: Issuer does not exist + - 1: Could not get the private key of the issuer + - 2: Failed to send transaction + created: + type: string + title: Created + notice_type: + type: string + enum: + - ScheduleEventError + const: ScheduleEventError + title: Notice Type + metainfo: + $ref: '#/components/schemas/ScheduleEventErrorMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: ScheduleEventErrorNotification + ScheduledEventIdResponse: + properties: + scheduled_event_id: + type: string + title: Scheduled Event Id + type: object + required: + - scheduled_event_id + title: ScheduledEventIdResponse + description: scheduled event (Response) + ScheduledEventResponse: + properties: + scheduled_event_id: + type: string + title: Scheduled Event Id + token_address: + type: string + title: Token Address + token_type: + $ref: '#/components/schemas/TokenType' + scheduled_datetime: + type: string + format: date-time + title: Scheduled Datetime + event_type: + $ref: '#/components/schemas/ScheduledEventType' + status: + type: integer + title: Status + data: + type: object + title: Data + created: + type: string + title: Created + type: object + required: + - scheduled_event_id + - token_address + - token_type + - scheduled_datetime + - event_type + - status + - data + - created + title: ScheduledEventResponse + description: scheduled event (Response) + ScheduledEventType: + type: string + enum: + - Update + const: Update + title: ScheduledEventType + SendTransactionErrorCode: + type: integer + enum: + - 2 + const: 2 + title: SendTransactionErrorCode + SendTransactionErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/SendTransactionErrorCode' + examples: + - 2 + title: + type: string + title: Title + examples: + - SendTransactionError + type: object + required: + - code + - title + title: SendTransactionErrorMetainfo + SendTransactionErrorResponse: + properties: + meta: + $ref: '#/components/schemas/SendTransactionErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: SendTransactionErrorResponse + ServiceUnavailableErrorCode: + type: integer + enum: + - 1 + const: 1 + title: ServiceUnavailableErrorCode + ServiceUnavailableErrorMetainfo: + properties: + code: + allOf: + - $ref: '#/components/schemas/ServiceUnavailableErrorCode' + examples: + - 1 + title: + type: string + title: Title + examples: + - ServiceUnavailableError + type: object + required: + - code + - title + title: ServiceUnavailableErrorMetainfo + ServiceUnavailableErrorResponse: + properties: + meta: + $ref: '#/components/schemas/ServiceUnavailableErrorMetainfo' + detail: + type: string + title: Detail + type: object + required: + - meta + - detail + title: ServiceUnavailableErrorResponse + SortOrder: + type: integer + enum: + - 0 + - 1 + title: SortOrder + TokenAddressResponse: + properties: + token_address: + type: string + title: Token Address + token_status: + type: integer + title: Token Status + type: object + required: + - token_address + - token_status + title: TokenAddressResponse + description: token address + TokenHolderBatchStatus: + type: string + enum: + - pending + - done + - failed + title: TokenHolderBatchStatus + TokenHoldersCollectionHolder: + properties: + account_address: + type: string + title: Account Address + description: Account address of token holder. + hold_balance: + type: integer + title: Hold Balance + description: Amount of balance.This includes balance/pending_transfer/exchange_balance/exchange_commitment. + locked_balance: + type: integer + title: Locked Balance + description: Amount of locked balance. + type: object + required: + - account_address + - hold_balance + - locked_balance + title: TokenHoldersCollectionHolder + TokenOperationLogResponse: + properties: + original_contents: + anyOf: + - type: object + - type: 'null' + title: Original Contents + description: original attributes before update + modified_contents: + type: object + title: Modified Contents + description: update attributes + operation_category: + $ref: '#/components/schemas/TokenUpdateOperationCategory' + created: + type: string + format: date-time + title: Created + type: object + required: + - modified_contents + - operation_category + - created + title: TokenOperationLogResponse + TokenType: + type: string + enum: + - IbetStraightBond + - IbetShare + title: TokenType + TokenUpdateOperationCategory: + type: string + enum: + - Issue + - Update + title: TokenUpdateOperationCategory + description: Operation category of update token + TransferApprovalHistoryResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + transfer_approval_history: + items: + $ref: '#/components/schemas/TransferApprovalTokenResponse' + type: array + title: Transfer Approval History + type: object + required: + - result_set + - transfer_approval_history + title: TransferApprovalHistoryResponse + description: transfer approval token history + TransferApprovalInfoMetaInfo: + properties: + id: + type: integer + title: Id + token_address: + type: string + title: Token Address + token_type: + anyOf: + - $ref: '#/components/schemas/TokenType' + - type: 'null' + type: object + required: + - id + - token_address + title: TransferApprovalInfoMetaInfo + TransferApprovalInfoNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 3.0 + minimum: 0.0 + title: Notice Code + description: |2 + - 0: Apply for transfer + - 1: Cancel transfer + - 2: Approve transfer + - 3: Escrow finished (Only occurs in security token escrow) + created: + type: string + title: Created + notice_type: + type: string + enum: + - TransferApprovalInfo + const: TransferApprovalInfo + title: Notice Type + metainfo: + $ref: '#/components/schemas/TransferApprovalInfoMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: TransferApprovalInfoNotification + TransferApprovalResponse: + properties: + issuer_address: + type: string + title: Issuer Address + token_address: + type: string + title: Token Address + application_count: + type: integer + title: Application Count + unapproved_count: + type: integer + title: Unapproved Count + escrow_finished_count: + type: integer + title: Escrow Finished Count + transferred_count: + type: integer + title: Transferred Count + canceled_count: + type: integer + title: Canceled Count + type: object + required: + - issuer_address + - token_address + - application_count + - unapproved_count + - escrow_finished_count + - transferred_count + - canceled_count + title: TransferApprovalResponse + description: transfer approval data + TransferApprovalStatus: + type: integer + enum: + - 0 + - 1 + - 2 + - 3 + title: TransferApprovalStatus + TransferApprovalTokenDetailResponse: + properties: + id: + type: integer + title: Id + token_address: + type: string + title: Token Address + exchange_address: + type: string + title: Exchange Address + application_id: + type: integer + title: Application Id + from_address: + type: string + title: From Address + from_address_personal_information: + anyOf: + - $ref: '#/components/schemas/PersonalInfo' + - type: 'null' + to_address: + type: string + title: To Address + to_address_personal_information: + anyOf: + - $ref: '#/components/schemas/PersonalInfo' + - type: 'null' + amount: + type: integer + title: Amount + application_datetime: + type: string + title: Application Datetime + application_blocktimestamp: + type: string + title: Application Blocktimestamp + approval_datetime: + anyOf: + - type: string + - type: 'null' + title: Approval Datetime + approval_blocktimestamp: + anyOf: + - type: string + - type: 'null' + title: Approval Blocktimestamp + cancellation_blocktimestamp: + anyOf: + - type: string + - type: 'null' + title: Cancellation Blocktimestamp + cancelled: + type: boolean + title: Cancelled + escrow_finished: + type: boolean + title: Escrow Finished + transfer_approved: + type: boolean + title: Transfer Approved + status: + type: integer + title: Status + issuer_cancelable: + type: boolean + title: Issuer Cancelable + type: object + required: + - id + - token_address + - exchange_address + - application_id + - from_address + - from_address_personal_information + - to_address + - to_address_personal_information + - amount + - application_datetime + - application_blocktimestamp + - approval_datetime + - approval_blocktimestamp + - cancellation_blocktimestamp + - cancelled + - escrow_finished + - transfer_approved + - status + - issuer_cancelable + title: TransferApprovalTokenDetailResponse + description: transfer approval token data + TransferApprovalTokenResponse: + properties: + id: + type: integer + title: Id + token_address: + type: string + title: Token Address + exchange_address: + type: string + title: Exchange Address + application_id: + type: integer + title: Application Id + from_address: + type: string + title: From Address + from_address_personal_information: + anyOf: + - $ref: '#/components/schemas/PersonalInfo' + - type: 'null' + to_address: + type: string + title: To Address + to_address_personal_information: + anyOf: + - $ref: '#/components/schemas/PersonalInfo' + - type: 'null' + amount: + type: integer + title: Amount + application_datetime: + type: string + title: Application Datetime + application_blocktimestamp: + type: string + title: Application Blocktimestamp + approval_datetime: + anyOf: + - type: string + - type: 'null' + title: Approval Datetime + approval_blocktimestamp: + anyOf: + - type: string + - type: 'null' + title: Approval Blocktimestamp + cancellation_blocktimestamp: + anyOf: + - type: string + - type: 'null' + title: Cancellation Blocktimestamp + cancelled: + type: boolean + title: Cancelled + escrow_finished: + type: boolean + title: Escrow Finished + transfer_approved: + type: boolean + title: Transfer Approved + status: + type: integer + title: Status + issuer_cancelable: + type: boolean + title: Issuer Cancelable + type: object + required: + - id + - token_address + - exchange_address + - application_id + - from_address + - from_address_personal_information + - to_address + - to_address_personal_information + - amount + - application_datetime + - application_blocktimestamp + - approval_datetime + - approval_blocktimestamp + - cancellation_blocktimestamp + - cancelled + - escrow_finished + - transfer_approved + - status + - issuer_cancelable + title: TransferApprovalTokenResponse + description: transfer approval token data + TransferApprovalsResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + transfer_approvals: + items: + $ref: '#/components/schemas/TransferApprovalResponse' + type: array + title: Transfer Approvals + type: object + required: + - result_set + - transfer_approvals + title: TransferApprovalsResponse + description: transfer approvals + TransferHistoryResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + transfer_history: + items: + $ref: '#/components/schemas/TransferResponse' + type: array + title: Transfer History + type: object + required: + - result_set + - transfer_history + title: TransferHistoryResponse + description: transfer history + TransferResponse: + properties: + transaction_hash: + type: string + title: Transaction Hash + token_address: + type: string + title: Token Address + from_address: + type: string + title: From Address + from_address_personal_information: + anyOf: + - $ref: '#/components/schemas/PersonalInfo' + - type: 'null' + to_address: + type: string + title: To Address + to_address_personal_information: + anyOf: + - $ref: '#/components/schemas/PersonalInfo' + - type: 'null' + amount: + type: integer + title: Amount + source_event: + allOf: + - $ref: '#/components/schemas/TransferSourceEventType' + description: Source Event + data: + anyOf: + - type: object + - type: 'null' + title: Data + description: Event data + block_timestamp: + type: string + title: Block Timestamp + type: object + required: + - transaction_hash + - token_address + - from_address + - from_address_personal_information + - to_address + - to_address_personal_information + - amount + - source_event + - data + - block_timestamp + title: TransferResponse + description: transfer data + TransferSourceEventType: + type: string + enum: + - Transfer + - Unlock + title: TransferSourceEventType + TxData: + properties: + hash: + type: string + title: Hash + description: Transaction hash + block_hash: + type: string + title: Block Hash + block_number: + type: integer + minimum: 0.0 + title: Block Number + transaction_index: + type: integer + minimum: 0.0 + title: Transaction Index + from_address: + type: string + title: From Address + to_address: + anyOf: + - type: string + - type: 'null' + title: To Address + type: object + required: + - hash + - block_hash + - block_number + - transaction_index + - from_address + - to_address + title: TxData + TxDataDetail: + properties: + hash: + type: string + title: Hash + description: Transaction hash + block_hash: + type: string + title: Block Hash + block_number: + type: integer + minimum: 0.0 + title: Block Number + transaction_index: + type: integer + minimum: 0.0 + title: Transaction Index + from_address: + type: string + title: From Address + to_address: + anyOf: + - type: string + - type: 'null' + title: To Address + contract_name: + anyOf: + - type: string + - type: 'null' + title: Contract Name + contract_function: + anyOf: + - type: string + - type: 'null' + title: Contract Function + contract_parameters: + anyOf: + - type: object + - type: 'null' + title: Contract Parameters + gas: + type: integer + minimum: 0.0 + title: Gas + gas_price: + type: integer + minimum: 0.0 + title: Gas Price + value: + type: integer + minimum: 0.0 + title: Value + nonce: + type: integer + minimum: 0.0 + title: Nonce + type: object + required: + - hash + - block_hash + - block_number + - transaction_index + - from_address + - to_address + - contract_name + - contract_function + - contract_parameters + - gas + - gas_price + - value + - nonce + title: TxDataDetail + TxDataListResponse: + properties: + result_set: + $ref: '#/components/schemas/ResultSet' + tx_data: + items: + $ref: '#/components/schemas/TxData' + type: array + title: Tx Data + type: object + required: + - result_set + - tx_data + title: TxDataListResponse + TxDataResponse: + allOf: + - $ref: '#/components/schemas/TxDataDetail' + title: TxDataResponse + UnlockInfoMetaInfo: + properties: + token_address: + type: string + title: Token Address + token_type: + $ref: '#/components/schemas/TokenType' + lock_address: + type: string + title: Lock Address + account_address: + type: string + title: Account Address + recipient_address: + type: string + title: Recipient Address + value: + type: integer + title: Value + data: + type: object + title: Data + type: object + required: + - token_address + - token_type + - lock_address + - account_address + - recipient_address + - value + - data + title: UnlockInfoMetaInfo + UnlockInfoNotification: + properties: + notice_id: + type: string + title: Notice Id + issuer_address: + type: string + title: Issuer Address + priority: + type: integer + title: Priority + notice_code: + type: integer + maximum: 0.0 + minimum: 0.0 + title: Notice Code + description: " - 0: Balance is unlocked\n" + created: + type: string + title: Created + notice_type: + type: string + enum: + - UnlockInfo + const: UnlockInfo + title: Notice Type + metainfo: + $ref: '#/components/schemas/UnlockInfoMetaInfo' + type: object + required: + - notice_id + - issuer_address + - priority + - notice_code + - created + - notice_type + - metainfo + title: UnlockInfoNotification + UpdateFreezeLogRequest: + properties: + account_address: + type: string + title: Account Address + description: Logging account address + eoa_password: + type: string + title: Eoa Password + description: Logging account key file password + log_message: + type: string + title: Log Message + description: Log message + type: object + required: + - account_address + - eoa_password + - log_message + title: UpdateFreezeLogRequest + description: Update freeze log schema (REQUEST) + UpdateTransferApprovalOperationType: + type: string + enum: + - approve + - cancel + title: UpdateTransferApprovalOperationType + UpdateTransferApprovalRequest: + properties: + operation_type: + $ref: '#/components/schemas/UpdateTransferApprovalOperationType' + type: object + required: + - operation_type + title: UpdateTransferApprovalRequest + description: Update Transfer Approval schema (Request) + UploadFileRequest: + properties: + relation: + anyOf: + - type: string + maxLength: 50 + - type: 'null' + title: Relation + file_name: + type: string + maxLength: 256 + title: File Name + content: + type: string + title: Content + description: |- + Base64-encoded content. + Max length of binary data before encoding is 100000000. + description: + anyOf: + - type: string + maxLength: 1000 + - type: 'null' + title: Description + label: + anyOf: + - type: string + maxLength: 200 + - type: 'null' + title: Label + type: object + required: + - file_name + - content + title: UploadFileRequest + description: Upload File schema (Request) + ValidationError: + properties: + loc: + items: + anyOf: + - type: string + - type: integer + type: array + title: Location + msg: + type: string + title: Message + type: + type: string + title: Error Type + type: object + required: + - loc + - msg + - type + title: ValidationError + ValueOperator: + type: integer + enum: + - 0 + - 1 + - 2 + title: ValueOperator +tags: + - name: root + description: '' + - name: common + description: Common functions + - name: account + description: Issuer account management + - name: notification + description: Notifications for accounts + - name: token_common + description: Common functions for tokens + - name: bond + description: Bond token management + - name: share + description: Share token management + - name: utility + description: Utility functions + - name: messaging + description: Messaging functions with external systems + - name: blockchain_explorer + description: Blockchain explorer diff --git a/hardhat.config.js b/hardhat.config.js new file mode 100644 index 00000000..6a1338f1 --- /dev/null +++ b/hardhat.config.js @@ -0,0 +1,15 @@ +/** @type import('hardhat/config').HardhatUserConfig */ +module.exports = { + networks: { + hardhat: { + chainId: 2017, + gasPrice: 0, + blockGasLimit: 800000000, + hardfork: "berlin", + throwOnTransactionFailures: false, + throwOnCallFailures: false, + allowBlocksWithSameTimestamp: true + }, + }, + solidity: "0.8.23", +}; \ No newline at end of file diff --git a/migrations/versions/0656c408ebbb_v24_6_0_feature_627.py b/migrations/versions/0656c408ebbb_v24_6_0_feature_627.py new file mode 100644 index 00000000..234d5078 --- /dev/null +++ b/migrations/versions/0656c408ebbb_v24_6_0_feature_627.py @@ -0,0 +1,74 @@ +"""v24_6_0_feature_627 + +Revision ID: 0656c408ebbb +Revises: 5954db6ba5f4 +Create Date: 2024-05-14 09:40:35.862686 + +""" + +from alembic import op +import sqlalchemy as sa + + +from app.database import get_db_schema + +# revision identifiers, used by Alembic. +revision = "0656c408ebbb" +down_revision = "5954db6ba5f4" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "idx_personal_info_history", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("account_address", sa.String(length=42), nullable=True), + sa.Column("issuer_address", sa.String(length=42), nullable=True), + sa.Column("event_type", sa.String(length=10), nullable=False), + sa.Column("personal_info", sa.JSON(), nullable=False), + sa.Column("created", sa.DateTime(), nullable=True), + sa.Column("modified", sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint("id"), + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_personal_info_history_account_address"), + "idx_personal_info_history", + ["account_address"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_personal_info_history_event_type"), + "idx_personal_info_history", + ["event_type"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_personal_info_history_issuer_address"), + "idx_personal_info_history", + ["issuer_address"], + unique=False, + schema=get_db_schema(), + ) + + +def downgrade(): + op.drop_index( + op.f("ix_idx_personal_info_history_issuer_address"), + table_name="idx_personal_info_history", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_personal_info_history_event_type"), + table_name="idx_personal_info_history", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_personal_info_history_account_address"), + table_name="idx_personal_info_history", + schema=get_db_schema(), + ) + op.drop_table("idx_personal_info_history", schema=get_db_schema()) diff --git a/migrations/versions/346d0e814893_v24_6_0_feature_606_1.py b/migrations/versions/346d0e814893_v24_6_0_feature_606_1.py new file mode 100644 index 00000000..96efde65 --- /dev/null +++ b/migrations/versions/346d0e814893_v24_6_0_feature_606_1.py @@ -0,0 +1,185 @@ +"""v24_6_0_feature_606_1 + +Revision ID: 346d0e814893 +Revises: a748dd5d4119 +Create Date: 2024-04-11 14:29:27.434855 + +""" + +from alembic import op +import sqlalchemy as sa + + +from app.database import get_db_schema + +# revision identifiers, used by Alembic. +revision = "346d0e814893" +down_revision = "a748dd5d4119" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "idx_delivery", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("exchange_address", sa.String(length=42), nullable=False), + sa.Column("delivery_id", sa.BigInteger(), nullable=False), + sa.Column("token_address", sa.String(length=42), nullable=False), + sa.Column("buyer_address", sa.String(length=42), nullable=False), + sa.Column("seller_address", sa.String(length=42), nullable=False), + sa.Column("amount", sa.BigInteger(), nullable=False), + sa.Column("agent_address", sa.String(length=42), nullable=False), + sa.Column("data", sa.Text(), nullable=False), + sa.Column("create_blocktimestamp", sa.DateTime(), nullable=False), + sa.Column("create_transaction_hash", sa.String(length=66), nullable=False), + sa.Column("cancel_blocktimestamp", sa.DateTime(), nullable=True), + sa.Column("cancel_transaction_hash", sa.String(length=66), nullable=True), + sa.Column("confirm_blocktimestamp", sa.DateTime(), nullable=True), + sa.Column("confirm_transaction_hash", sa.String(length=66), nullable=True), + sa.Column("finish_blocktimestamp", sa.DateTime(), nullable=True), + sa.Column("finish_transaction_hash", sa.String(length=66), nullable=True), + sa.Column("abort_blocktimestamp", sa.DateTime(), nullable=True), + sa.Column("abort_transaction_hash", sa.String(length=66), nullable=True), + sa.Column("confirmed", sa.Boolean(), nullable=False), + sa.Column("valid", sa.Boolean(), nullable=False), + sa.Column("status", sa.BigInteger(), nullable=False), + sa.Column("created", sa.DateTime(), nullable=True), + sa.Column("modified", sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint("id"), + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_abort_transaction_hash"), + "idx_delivery", + ["abort_transaction_hash"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_agent_address"), + "idx_delivery", + ["agent_address"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_cancel_transaction_hash"), + "idx_delivery", + ["cancel_transaction_hash"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_confirm_transaction_hash"), + "idx_delivery", + ["confirm_transaction_hash"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_create_transaction_hash"), + "idx_delivery", + ["create_transaction_hash"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_delivery_id"), + "idx_delivery", + ["delivery_id"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_exchange_address"), + "idx_delivery", + ["exchange_address"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_finish_transaction_hash"), + "idx_delivery", + ["finish_transaction_hash"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_seller_address"), + "idx_delivery", + ["seller_address"], + unique=False, + schema=get_db_schema(), + ) + op.create_index( + op.f("ix_idx_delivery_token_address"), + "idx_delivery", + ["token_address"], + unique=False, + schema=get_db_schema(), + ) + op.create_table( + "idx_delivery_block_number", + sa.Column("exchange_address", sa.String(length=42), nullable=False), + sa.Column("latest_block_number", sa.BigInteger(), nullable=False), + sa.Column("created", sa.DateTime(), nullable=True), + sa.Column("modified", sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint("exchange_address"), + schema=get_db_schema(), + ) + + +def downgrade(): + op.drop_table("idx_delivery_block_number", schema=get_db_schema()) + op.drop_index( + op.f("ix_idx_delivery_token_address"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_seller_address"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_finish_transaction_hash"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_exchange_address"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_delivery_id"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_create_transaction_hash"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_confirm_transaction_hash"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_cancel_transaction_hash"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_agent_address"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_index( + op.f("ix_idx_delivery_abort_transaction_hash"), + table_name="idx_delivery", + schema=get_db_schema(), + ) + op.drop_table("idx_delivery", schema=get_db_schema()) diff --git a/migrations/versions/4704529e8fe0_v24_6_0_feature_637.py b/migrations/versions/4704529e8fe0_v24_6_0_feature_637.py new file mode 100644 index 00000000..b25acf99 --- /dev/null +++ b/migrations/versions/4704529e8fe0_v24_6_0_feature_637.py @@ -0,0 +1,37 @@ +"""v24_6_0_feature_637 + +Revision ID: 4704529e8fe0 +Revises: 0656c408ebbb +Create Date: 2024-06-04 17:46:03.131011 + +""" + +from alembic import op +import sqlalchemy as sa +from sqlalchemy import delete + + +from app.database import get_db_schema +from app.model.db import IDXPersonalInfoHistory + +# revision identifiers, used by Alembic. +revision = "4704529e8fe0" +down_revision = "0656c408ebbb" +branch_labels = None +depends_on = None + + +def upgrade(): + # Delete all `idx_personal_info_history` data + op.get_bind().execute(delete(IDXPersonalInfoHistory)) + op.add_column( + "idx_personal_info_history", + sa.Column("block_timestamp", sa.DateTime(), nullable=True), + schema=get_db_schema(), + ) + + +def downgrade(): + op.drop_column( + "idx_personal_info_history", "block_timestamp", schema=get_db_schema() + ) diff --git a/migrations/versions/5954db6ba5f4_v24_6_0_feature_606_2.py b/migrations/versions/5954db6ba5f4_v24_6_0_feature_606_2.py new file mode 100644 index 00000000..1be2d026 --- /dev/null +++ b/migrations/versions/5954db6ba5f4_v24_6_0_feature_606_2.py @@ -0,0 +1,37 @@ +"""v24_6_0_feature_606_2 + +Revision ID: 5954db6ba5f4 +Revises: 346d0e814893 +Create Date: 2024-04-12 21:45:53.594057 + +""" + +from alembic import op +import sqlalchemy as sa + + +from app.database import get_db_schema + +# revision identifiers, used by Alembic. +revision = "5954db6ba5f4" +down_revision = "346d0e814893" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "dvp_agent_account", + sa.Column("account_address", sa.String(length=42), nullable=False), + sa.Column("keyfile", sa.JSON(), nullable=True), + sa.Column("eoa_password", sa.String(length=2000), nullable=True), + sa.Column("is_deleted", sa.Boolean(), nullable=True), + sa.Column("created", sa.DateTime(), nullable=True), + sa.Column("modified", sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint("account_address"), + schema=get_db_schema(), + ) + + +def downgrade(): + op.drop_table("dvp_agent_account", schema=get_db_schema()) diff --git a/migrations/versions/a748dd5d4119_v24_6_0_feature_610.py b/migrations/versions/a748dd5d4119_v24_6_0_feature_610.py new file mode 100644 index 00000000..72815ce8 --- /dev/null +++ b/migrations/versions/a748dd5d4119_v24_6_0_feature_610.py @@ -0,0 +1,27 @@ +"""v24_6_0_feature_610 + +Revision ID: a748dd5d4119 +Revises: cb24da38646e +Create Date: 2024-04-07 00:40:00.047349 + +""" + +from alembic import op +from sqlalchemy import delete + +from app.model.db import TokenCache + +# revision identifiers, used by Alembic. +revision = "a748dd5d4119" +down_revision = "cb24da38646e" +branch_labels = None +depends_on = None + + +def upgrade(): + # Delete all `token_cache` data + op.get_bind().execute(delete(TokenCache)) + + +def downgrade(): + pass diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..e8d28e1b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3459 @@ +{ + "name": "ibet-Prime", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "hardhat": "^2.22.3" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dev": true, + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@nomicfoundation/edr": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.3.5.tgz", + "integrity": "sha512-dPSM9DuI1sr71gqWUMgLo8MjHQWO4+WNDm3iWaT6P4vUFJReZX5qwA5X+3UwIPBry8GvNY084u7yWUvB3/8rqA==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "optionalDependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.3.5", + "@nomicfoundation/edr-darwin-x64": "0.3.5", + "@nomicfoundation/edr-linux-arm64-gnu": "0.3.5", + "@nomicfoundation/edr-linux-arm64-musl": "0.3.5", + "@nomicfoundation/edr-linux-x64-gnu": "0.3.5", + "@nomicfoundation/edr-linux-x64-musl": "0.3.5", + "@nomicfoundation/edr-win32-x64-msvc": "0.3.5" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.5.tgz", + "integrity": "sha512-gIXUIiPMUy6roLHpNlxf15DumU7/YhffUf7XIB+WUjMecaySfTGyZsTGnCMJZqrDyiYqWPyPKwCV/2u/jqFAUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.5.tgz", + "integrity": "sha512-0MrpOCXUK8gmplpYZ2Cy0holHEylvWoNeecFcrP2WJ5DLQzrB23U5JU2MvUzOJ7aL76Za1VXNBWi/UeTWdHM+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.5.tgz", + "integrity": "sha512-aw9f7AZMiY1dZFNePJGKho2k+nEgFgzUAyyukiKfSqUIMXoFXMf1U3Ujv848czrSq9c5XGcdDa2xnEf3daU3xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.5.tgz", + "integrity": "sha512-cVFRQjyABBlsbDj+XTczYBfrCHprZ6YNzN8gGGSqAh+UGIJkAIRomK6ar27GyJLNx3HkgbuDoi/9kA0zOo/95w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.5.tgz", + "integrity": "sha512-CjOg85DfR1Vt0fQWn5U0qi26DATK9tVzo3YOZEyI0JBsnqvk43fUTPv3uUAWBrPIRg5O5kOc9xG13hSpCBBxBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.5.tgz", + "integrity": "sha512-hvX8bBGpBydAVevzK8jsu2FlqVZK1RrCyTX6wGHnltgMuBaoGLHYtNHiFpteOaJw2byYMiORc2bvj+98LhJ0Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.5.tgz", + "integrity": "sha512-IJXjW13DY5UPsx/eG5DGfXtJ7Ydwrvw/BTZ2Y93lRLHzszVpSmeVmlxjZP5IW2afTSgMLaAAsqNw4NhppRGN8A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-util": "9.0.4" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", + "dev": true, + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz", + "integrity": "sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg==", + "dev": true, + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.1", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.1", + "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.1", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.1", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.1", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.1", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.1", + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.1.1", + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.1.1", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.1" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz", + "integrity": "sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz", + "integrity": "sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-freebsd-x64": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz", + "integrity": "sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz", + "integrity": "sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz", + "integrity": "sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz", + "integrity": "sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz", + "integrity": "sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-arm64-msvc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz", + "integrity": "sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-ia32-msvc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz", + "integrity": "sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz", + "integrity": "sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@scure/base": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==", + "dev": true, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dev": true, + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dev": true, + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dev": true, + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dev": true, + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dev": true, + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dev": true, + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.12.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.3.tgz", + "integrity": "sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true, + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dev": true, + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true + }, + "node_modules/commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dev": true, + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/hardhat": { + "version": "2.22.3", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.3.tgz", + "integrity": "sha512-k8JV2ECWNchD6ahkg2BR5wKVxY0OiKot7fuxiIpRK0frRqyOljcR2vKwgWSLw6YIeDcNNA4xybj7Og7NSxr2hA==", + "dev": true, + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/edr": "^0.3.5", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-tx": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "^5.1.0", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", + "chalk": "^2.4.2", + "chokidar": "^3.4.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^2.1.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "glob": "7.2.0", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.7.3", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tsort": "0.0.1", + "undici": "^5.14.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/bootstrap.js" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "dev": true + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dev": true, + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "dev": true, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "dev": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dev": true, + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "8.1.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true + }, + "node_modules/node-gyp-build": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true + }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/solc": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", + "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", + "dev": true, + "dependencies": { + "command-exists": "^1.2.8", + "commander": "3.0.2", + "follow-redirects": "^1.12.1", + "fs-extra": "^0.30.0", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "require-from-string": "^2.0.0", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solcjs" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/solc/node_modules/fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "node_modules/solc/node_modules/jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dev": true, + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dev": true, + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "dev": true + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", + "dev": true + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dev": true, + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..abb4e645 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "hardhat": "^2.22.3" + } +} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 50fbfd82..fb7df888 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,88 +1,88 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiohttp" -version = "3.9.3" +version = "3.9.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, - {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, - {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, - {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, - {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, - {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, - {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, - {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, - {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, - {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, - {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, - {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, - {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, - {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, - {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, - {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, - {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, - {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, - {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, - {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, - {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, - {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, - {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, - {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, - {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, - {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, - {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, - {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, - {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, - {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, - {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, - {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, - {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, - {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, - {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, - {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, ] [package.dependencies] @@ -130,24 +130,24 @@ tz = ["backports.zoneinfo"] [[package]] name = "annotated-types" -version = "0.6.0" +version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] [[package]] name = "anyio" -version = "4.3.0" +version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, ] [package.dependencies] @@ -332,33 +332,33 @@ files = [ [[package]] name = "black" -version = "24.2.0" +version = "24.4.2" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, - {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, - {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, - {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, - {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, - {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, - {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, - {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, - {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, - {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, - {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, - {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, - {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, - {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, - {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, - {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, - {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, - {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, - {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, - {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, - {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, - {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, ] [package.dependencies] @@ -376,17 +376,17 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.34.59" +version = "1.34.116" description = "The AWS SDK for Python" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "boto3-1.34.59-py3-none-any.whl", hash = "sha256:004e67b078be58d34469406f93cc8b95bc43becef4bbe44523a0b8e51f84c668"}, - {file = "boto3-1.34.59.tar.gz", hash = "sha256:162edf182e53c198137a28432a626dba103f787a8f5000ed4758b73ccd203fa0"}, + {file = "boto3-1.34.116-py3-none-any.whl", hash = "sha256:e7f5ab2d1f1b90971a2b9369760c2c6bae49dae98c084a5c3f5c78e3968ace15"}, + {file = "boto3-1.34.116.tar.gz", hash = "sha256:53cb8aeb405afa1cd2b25421e27a951aeb568026675dec020587861fac96ac87"}, ] [package.dependencies] -botocore = ">=1.34.59,<1.35.0" +botocore = ">=1.34.116,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -395,22 +395,22 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.59" +version = "1.34.116" description = "Low-level, data-driven core of boto 3." optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "botocore-1.34.59-py3-none-any.whl", hash = "sha256:4bc112dafb1679ab571117593f7656604726a3da0e5ae5bad00ea772fa40e75c"}, - {file = "botocore-1.34.59.tar.gz", hash = "sha256:24edb4d21d7c97dea0c6c4a80d36b3809b1443a30b0bd5e317d6c319dfac823f"}, + {file = "botocore-1.34.116-py3-none-any.whl", hash = "sha256:ec4d42c816e9b2d87a2439ad277e7dda16a4a614ef6839cf66f4c1a58afa547c"}, + {file = "botocore-1.34.116.tar.gz", hash = "sha256:269cae7ba99081519a9f87d7298e238d9e68ba94eb4f8ddfa906224c34cb8b6c"}, ] [package.dependencies] jmespath = ">=0.7.1,<2.0.0" python-dateutil = ">=2.1,<3.0.0" -urllib3 = {version = ">=1.25.4,<2.1", markers = "python_version >= \"3.10\""} +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.19.19)"] +crt = ["awscrt (==0.20.9)"] [[package]] name = "certifi" @@ -597,6 +597,100 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "ckzg" +version = "1.0.2" +description = "Python bindings for C-KZG-4844" +optional = false +python-versions = "*" +files = [ + {file = "ckzg-1.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bdd082bc0f2a595e3546658ecbe1ff78fe65b0ab7e619a8197a62d94f46b5b46"}, + {file = "ckzg-1.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50ca4af4e2f1a1e8b0a7e97b3aef39dedbb0d52d90866ece424f13f8df1b5972"}, + {file = "ckzg-1.0.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e9dc671b0a307ea65d0a216ca496c272dd3c1ed890ddc2a306da49b0d8ffc83"}, + {file = "ckzg-1.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d95e97a0d0f7758119bb905fb5688222b1556de465035614883c42fe4a047d1f"}, + {file = "ckzg-1.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27261672154cbd477d84d289845b0022fbdbe2ba45b7a2a2051c345fa04c8334"}, + {file = "ckzg-1.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c16d5ee1ddbbbad0367ff970b3ec9f6d1879e9f928023beda59ae9e16ad99e4c"}, + {file = "ckzg-1.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:09043738b029bdf4fdc82041b395cfc6f5b5cf63435e5d4d685d24fd14c834d3"}, + {file = "ckzg-1.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3c0afa232d2312e3101aaddb6971b486b0038a0f9171500bc23143f5749eff55"}, + {file = "ckzg-1.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:96e8281b6d58cf91b9559e1bd38132161d63467500838753364c68e825df2e2c"}, + {file = "ckzg-1.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b874167de1d6de72890a2ad5bd9aa7adbddc41c3409923b59cf4ef27f83f79da"}, + {file = "ckzg-1.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d2ccd68b0743e20e853e31a08da490a8d38c7f12b9a0c4ee63ef5afa0dc2427"}, + {file = "ckzg-1.0.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e8d534ddbe785c44cf1cd62ee32d78b4310d66dd70e42851f5468af655b81f5"}, + {file = "ckzg-1.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c732cda00c76b326f39ae97edfc6773dd231b7c77288b38282584a7aee77c3a7"}, + {file = "ckzg-1.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abc5a27284db479ead4c053ff086d6e222914f1b0aa08b80eabfa116dbed4f7a"}, + {file = "ckzg-1.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6bd5006cb3e802744309450183087a6594d50554814eee19065f7064dff7b05"}, + {file = "ckzg-1.0.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3594470134eda7adf2813ad3f1da55ced98c8a393262f47ce3890c5afa05b23e"}, + {file = "ckzg-1.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fea56f39e48b60c1ff6f751c47489e353d1bd95cae65c429cf5f87735d794431"}, + {file = "ckzg-1.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:f769eb2e1056ca396462460079f6849c778f58884bb24b638ff7028dd2120b65"}, + {file = "ckzg-1.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e3cb2f8c767aee57e88944f90848e8689ce43993b9ff21589cfb97a562208fe7"}, + {file = "ckzg-1.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b29889f5bc5db530f766871c0ff4133e7270ecf63aaa3ca756d3b2731980802"}, + {file = "ckzg-1.0.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfcc70fb76b3d36125d646110d5001f2aa89c1c09ff5537a4550cdb7951f44d4"}, + {file = "ckzg-1.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ca8a256cdd56d06bc5ef24caac64845240dbabca402c5a1966d519b2514b4ec"}, + {file = "ckzg-1.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ea91b0236384f93ad1df01d530672f09e254bd8c3cf097ebf486aebb97f6c8c"}, + {file = "ckzg-1.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:65311e72780105f239d1d66512629a9f468b7c9f2609b8567fc68963ac638ef9"}, + {file = "ckzg-1.0.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0d7600ce7a73ac41d348712d0c1fe5e4cb6caa329377064cfa3a6fd8fbffb410"}, + {file = "ckzg-1.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19893ee7bd7da8688382cb134cb9ee7bce5c38e3a9386e3ed99bb010487d2d17"}, + {file = "ckzg-1.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:c3e1a9a72695e777497e95bb2213316a1138f82d1bb5d67b9c029a522d24908e"}, + {file = "ckzg-1.0.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2f59da9cb82b6a4be615f2561a255731eededa7ecd6ba4b2f2dedfc918ef137"}, + {file = "ckzg-1.0.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c915e1f2ef51657c3255d8b1e2aea6e0b93348ae316b2b79eaadfb17ad8f514e"}, + {file = "ckzg-1.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcc0d2031fcabc4be37e9e602c926ef9347238d2f58c1b07e0c147f60b9e760b"}, + {file = "ckzg-1.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cdaad2745425d7708e76e8e56a52fdaf5c5cc1cfefd5129d24ff8dbe06a012d"}, + {file = "ckzg-1.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:1ec775649daade1b93041aac9c1660c2ad9828b57ccd2eeb5a3074d8f05e544a"}, + {file = "ckzg-1.0.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:02f9cc3e38b3702ec5895a1ebf927fd02b8f5c2f93c7cb9e438581b5b74472c8"}, + {file = "ckzg-1.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0e816af31951b5e94e6bc069f21fe783427c190526e0437e16c4488a34ddcacc"}, + {file = "ckzg-1.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:651ba33ee2d7fefff14ca519a72996b733402f8b043fbfef12d5fe2a442d86d8"}, + {file = "ckzg-1.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:489763ad92e2175fb6ab455411f03ec104c630470d483e11578bf2e00608f283"}, + {file = "ckzg-1.0.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69e1376284e9a5094d7c4d3e552202d6b32a67c5acc461b0b35718d8ec5c7363"}, + {file = "ckzg-1.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb9d0b09ca1bdb5955b626d6645f811424ae0fcab47699a1a938a3ce0438c25f"}, + {file = "ckzg-1.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d87a121ace8feb6c9386f247e7e36ef55e584fc8a6b1bc2c60757a59c1efe364"}, + {file = "ckzg-1.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:97c27153fab853f017fed159333b27beeb2e0da834c92c9ecdc26d0e5c3983b3"}, + {file = "ckzg-1.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b26799907257c39471cb3665f66f7630797140131606085c2c94a7094ab6ddf2"}, + {file = "ckzg-1.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:283a40c625222560fda3dcb912b666f7d50f9502587b73c4358979f519f1c961"}, + {file = "ckzg-1.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:5f029822d27c52b9c3dbe5706408b099da779f10929be0422a09a34aa026a872"}, + {file = "ckzg-1.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edaea8fb50b01c6c19768d9305ad365639a8cd804754277d5108dcae4808f00b"}, + {file = "ckzg-1.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:27be65c88d5d773a30e6f198719cefede7e25cad807384c3d65a09c11616fc9d"}, + {file = "ckzg-1.0.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9ac729c5c6f3d2c030c0bc8c9e10edc253e36f002cfe227292035009965d349"}, + {file = "ckzg-1.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1528bc2b95aac6d184a90b023602c40d7b11b577235848c1b5593c00cf51d37"}, + {file = "ckzg-1.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:071dc7fc179316ce1bfabaa056156e4e84f312c4560ab7b9529a3b9a84019df3"}, + {file = "ckzg-1.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:895044069de7010be6c7ee703f03fd7548267a0823cf60b9dd26ec50267dd9e8"}, + {file = "ckzg-1.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ed8c99cd3d9af596470e0481fd58931007288951719bad026f0dd486dd0ec11"}, + {file = "ckzg-1.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:74d87eafe561d4bfb544a4f3419d26c56ad7de00f39789ef0fdb09515544d12e"}, + {file = "ckzg-1.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:54d71e5ca416bd51c543f9f51e426e6792f8a0280b83aef92faad1b826f401ea"}, + {file = "ckzg-1.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:da2d9988781a09a4577ee7ea8f51fe4a94b4422789a523164f5ba3118566ad41"}, + {file = "ckzg-1.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d9e030af7d6acdcb356fddfb095048bc8e880fe4cd70ff2206c64f33bf384a0d"}, + {file = "ckzg-1.0.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:145ae31c3d499d1950567bd636dc5b24292b600296b9deb5523bc20d8f7b51c3"}, + {file = "ckzg-1.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d81e68e84d80084da298471ad5eaddfcc1cf73545cb24e9453550c8186870982"}, + {file = "ckzg-1.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c67064bbbeba1a6892c9c80b3d0c2a540ff48a5ca5356fdb2a8d998b264e43e6"}, + {file = "ckzg-1.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:99694917eb6decefc0d330d9887a89ea770824b2fa76eb830bab5fe57ea5c20c"}, + {file = "ckzg-1.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fca227ce0ce3427254a113fdb3aed5ecd99c1fc670cb0c60cc8a2154793678e4"}, + {file = "ckzg-1.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a66a690d3d1801085d11de6825df47a99b465ff32dbe90be4a3c9f43c577da96"}, + {file = "ckzg-1.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:272adfe471380d10e4a0e1639d877e504555079a60233dd82249c799b15be81e"}, + {file = "ckzg-1.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f37be0054ebb4b8ac6e6d5267290b239b09e7ddc611776051b4c3c4032d161ba"}, + {file = "ckzg-1.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:611c03a170f0f746180eeb0cc28cdc6f954561b8eb9013605a046de86520ee6b"}, + {file = "ckzg-1.0.2-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75b2f0ab341f3c33702ce64e1c101116c7462a25686d0b1a0193ca654ad4f96e"}, + {file = "ckzg-1.0.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab29fc61fbd32096b82b02e6b18ae0d7423048d3540b7b90805b16ae10bdb769"}, + {file = "ckzg-1.0.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e43741e7453262aa3ba1754623d7864250b33751bd850dd548e3ed6bd1911093"}, + {file = "ckzg-1.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:155eacc237cb28c9eafda1c47a89e6e4550f1c2e711f2eee21e0bb2f4df75546"}, + {file = "ckzg-1.0.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31d7fbe396a51f43375e38c31bc3a96c7996882582f95f3fcfd54acfa7b3ce6"}, + {file = "ckzg-1.0.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d3d049186c9966e9140de39a9979d7adcfe22f8b02d2852c94d3c363235cc18"}, + {file = "ckzg-1.0.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88728fbd410d61bd5d655ac50b842714c38bc34ff717f73592132d28911fc88e"}, + {file = "ckzg-1.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:052d302058d72431acc9dd4a9c76854c8dfce10c698deef5252884e32a1ac7bf"}, + {file = "ckzg-1.0.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:633110a9431231664be2ad32baf10971547f18289d33967654581b9ae9c94a7e"}, + {file = "ckzg-1.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f439c9e5297ae29a700f6d55de1525e2e295dbbb7366f0974c8702fca9e536b9"}, + {file = "ckzg-1.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:94f7eb080c00c0ccbd4fafad69f0b35b624a6a229a28e11d365b60b58a072832"}, + {file = "ckzg-1.0.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f876783ec654b7b9525503c2a0a1b086e5d4f52ff65cac7e8747769b0c2e5468"}, + {file = "ckzg-1.0.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7e039800e50592580171830e788ef4a1d6bb54300d074ae9f9119e92aefc568"}, + {file = "ckzg-1.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a8cccf0070a29bc01493179db2e61220ee1a6cb17f8ea41c68a2f043ace87f"}, + {file = "ckzg-1.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4f86cef801d7b0838e17b6ee2f2c9e747447d91ad1220a701baccdf7ef11a3c8"}, + {file = "ckzg-1.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2433a89af4158beddebbdd66fae95b34d40f2467bee8dc40df0333de5e616b5f"}, + {file = "ckzg-1.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c49d5dc0918ad912777720035f9820bdbb6c7e7d1898e12506d44ab3c938d525"}, + {file = "ckzg-1.0.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:331d49bc72430a3f85ea6ecb55a0d0d65f66a21d61af5783b465906a741366d5"}, + {file = "ckzg-1.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86627bc33bc63b8de869d7d5bfa9868619a4f3e4e7082103935c52f56c66b5"}, + {file = "ckzg-1.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab6a2ba2706b5eaa1ce6bc7c4e72970bf9587e2e0e482e5fb4df1996bccb7a40"}, + {file = "ckzg-1.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8bca5e7c38d913fabc24ad09545f78ba23cfc13e1ac8250644231729ca908549"}, + {file = "ckzg-1.0.2.tar.gz", hash = "sha256:4295acc380f8d42ebea4a4a0a68c424a322bb335a33bad05c72ead8cbb28d118"}, +] + [[package]] name = "click" version = "8.1.7" @@ -680,63 +774,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.3" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -872,21 +966,56 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.1.1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, + {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + [[package]] name = "eth-abi" -version = "5.0.1" +version = "5.1.0" description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth_abi-5.0.1-py3-none-any.whl", hash = "sha256:521960d8b4beee514958e1774951dc6b48176aa274e3bd8b166f6921453047ef"}, - {file = "eth_abi-5.0.1.tar.gz", hash = "sha256:e9425110c6120c585c9f0db2e8a33d76c4b886b148a65e68fc0035d3917a3b9c"}, + {file = "eth_abi-5.1.0-py3-none-any.whl", hash = "sha256:84cac2626a7db8b7d9ebe62b0fdca676ab1014cc7f777189e3c0cd721a4c16d8"}, + {file = "eth_abi-5.1.0.tar.gz", hash = "sha256:33ddd756206e90f7ddff1330cc8cac4aa411a824fe779314a0a52abea2c8fc14"}, ] [package.dependencies] eth-typing = ">=3.0.0" eth-utils = ">=2.0.0" -parsimonious = ">=0.9.0,<0.10.0" +parsimonious = ">=0.10.0,<0.11.0" [package.extras] dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "hypothesis (>=4.18.2,<5.0.0)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-pythonpath (>=0.7.1)", "pytest-timeout (>=2.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] @@ -896,28 +1025,30 @@ tools = ["hypothesis (>=4.18.2,<5.0.0)"] [[package]] name = "eth-account" -version = "0.11.0" +version = "0.13.0" description = "eth-account: Sign Ethereum transactions and messages with local private keys" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth-account-0.11.0.tar.gz", hash = "sha256:2ffc7a0c7538053a06a7d11495c16c7ad9897dd42be0f64ca7551e9f6e0738c3"}, - {file = "eth_account-0.11.0-py3-none-any.whl", hash = "sha256:76dd261ea096ee09e51455b0a4c99f22185516fdc062f63df0817c28f605e430"}, + {file = "eth_account-0.13.0-py3-none-any.whl", hash = "sha256:84d27664038c68e6bee28fa5de803c3b629dc5d97cd61d12e5f442c561a5b8af"}, + {file = "eth_account-0.13.0.tar.gz", hash = "sha256:ccea4383d9d37b46e17c977b0a5ea397cef1ac1ad3330431ae4b0c10b62d4fcd"}, ] [package.dependencies] bitarray = ">=2.4.0" +ckzg = ">=0.4.3" eth-abi = ">=4.0.0-b.2" eth-keyfile = ">=0.6.0" eth-keys = ">=0.4.0" -eth-rlp = ">=0.3.0" +eth-rlp = ">=2.1.0" eth-utils = ">=2.0.0" -hexbytes = ">=0.1.0,<0.4.0" +hexbytes = ">=1.2.0" +pydantic = ">=2.0.0" rlp = ">=1.0.0" [package.extras] -dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "coverage", "hypothesis (>=4.18.0,<5)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] -docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "coverage", "hypothesis (>=4.18.0,<5)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] test = ["coverage", "hypothesis (>=4.18.0,<5)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] @@ -943,13 +1074,13 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "eth-keyfile" -version = "0.7.0" +version = "0.8.1" description = "eth-keyfile: A library for handling the encrypted keyfiles used to store ethereum private keys" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth-keyfile-0.7.0.tar.gz", hash = "sha256:6bdb8110c3a50439deb68a04c93c9d5ddd5402353bfae1bf4cfca1d6dff14fcf"}, - {file = "eth_keyfile-0.7.0-py3-none-any.whl", hash = "sha256:6a89b231a2fe250c3a8f924f2695bb9cce33ddd0d6f7ebbcdacd183d7f83d537"}, + {file = "eth_keyfile-0.8.1-py3-none-any.whl", hash = "sha256:65387378b82fe7e86d7cb9f8d98e6d639142661b2f6f490629da09fddbef6d64"}, + {file = "eth_keyfile-0.8.1.tar.gz", hash = "sha256:9708bc31f386b52cca0969238ff35b1ac72bd7a7186f2a84b86110d3c973bec1"}, ] [package.dependencies] @@ -964,13 +1095,13 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "eth-keys" -version = "0.5.0" +version = "0.5.1" description = "eth-keys: Common API for Ethereum key operations" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth-keys-0.5.0.tar.gz", hash = "sha256:a0abccb83f3d84322591a2c047a1e3aa52ea86b185fa3e82ce311d120ca2791e"}, - {file = "eth_keys-0.5.0-py3-none-any.whl", hash = "sha256:b2bed3ff3bcede68cc0cd4458c7147baaeaac1211a1efdb6ca019f9d3d989f2b"}, + {file = "eth_keys-0.5.1-py3-none-any.whl", hash = "sha256:ad13d920a2217a49bed3a1a7f54fb0980f53caf86d3bbab2139fd3330a17b97e"}, + {file = "eth_keys-0.5.1.tar.gz", hash = "sha256:2b587e4bbb9ac2195215a7ab0c0fb16042b17d4ec50240ed670bbb8f53da7a48"}, ] [package.dependencies] @@ -979,26 +1110,25 @@ eth-utils = ">=2" [package.extras] coincurve = ["coincurve (>=12.0.0)"] -dev = ["asn1tools (>=0.146.2)", "build (>=0.9.0)", "bumpversion (>=0.5.3)", "coincurve (>=12.0.0)", "eth-hash[pysha3]", "factory-boy (>=3.0.1)", "hypothesis (>=5.10.3,<6)", "ipython", "pre-commit (>=3.4.0)", "pyasn1 (>=0.4.5)", "pytest (>=7.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +dev = ["asn1tools (>=0.146.2)", "build (>=0.9.0)", "bumpversion (>=0.5.3)", "coincurve (>=12.0.0)", "eth-hash[pysha3]", "factory-boy (>=3.0.1)", "hypothesis (>=5.10.3)", "ipython", "pre-commit (>=3.4.0)", "pyasn1 (>=0.4.5)", "pytest (>=7.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] docs = ["towncrier (>=21,<22)"] -test = ["asn1tools (>=0.146.2)", "eth-hash[pysha3]", "factory-boy (>=3.0.1)", "hypothesis (>=5.10.3,<6)", "pyasn1 (>=0.4.5)", "pytest (>=7.0.0)"] +test = ["asn1tools (>=0.146.2)", "eth-hash[pysha3]", "factory-boy (>=3.0.1)", "hypothesis (>=5.10.3)", "pyasn1 (>=0.4.5)", "pytest (>=7.0.0)"] [[package]] name = "eth-rlp" -version = "1.0.1" +version = "2.1.0" description = "eth-rlp: RLP definitions for common Ethereum objects in Python" optional = false python-versions = ">=3.8, <4" files = [ - {file = "eth-rlp-1.0.1.tar.gz", hash = "sha256:d61dbda892ee1220f28fb3663c08f6383c305db9f1f5624dc585c9cd05115027"}, - {file = "eth_rlp-1.0.1-py3-none-any.whl", hash = "sha256:dd76515d71654277377d48876b88e839d61553aaf56952e580bb7cebef2b1517"}, + {file = "eth-rlp-2.1.0.tar.gz", hash = "sha256:d5b408a8cd20ed496e8e66d0559560d29bc21cee482f893936a1f05d0dddc4a0"}, + {file = "eth_rlp-2.1.0-py3-none-any.whl", hash = "sha256:6f476eb7e37d81feaba5d98aed887e467be92648778c44b19fe594aea209cde1"}, ] [package.dependencies] eth-utils = ">=2.0.0" -hexbytes = ">=0.1.0,<1" +hexbytes = ">=1.2.0" rlp = ">=0.6.0" -typing-extensions = {version = ">=4.0.1", markers = "python_version <= \"3.11\""} [package.extras] dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] @@ -1007,13 +1137,13 @@ test = ["eth-hash[pycryptodome]", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "eth-typing" -version = "4.0.0" +version = "4.2.3" description = "eth-typing: Common type annotations for ethereum python packages" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth-typing-4.0.0.tar.gz", hash = "sha256:9af0b6beafbc5c2e18daf19da5f5a68315023172c4e79d149e12ad10a3d3f731"}, - {file = "eth_typing-4.0.0-py3-none-any.whl", hash = "sha256:7e556bea322b6e8c0a231547b736c258e10ce9eed5ddc254f51031b12af66a16"}, + {file = "eth_typing-4.2.3-py3-none-any.whl", hash = "sha256:b2df49fa89d2e85f2cc3fb1c903b0cd183d524f7a045e3db8cc720cf41adcd3d"}, + {file = "eth_typing-4.2.3.tar.gz", hash = "sha256:8ee3ae7d4136d14fcb955c34f9dbef8e52170984d4dc68c0ab0d61621eab29d8"}, ] [package.extras] @@ -1023,13 +1153,13 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "eth-utils" -version = "3.0.0" +version = "4.1.1" description = "eth-utils: Common utility functions for python code that interacts with Ethereum" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "eth-utils-3.0.0.tar.gz", hash = "sha256:8721869568448349bceae63c277b75758d11e0dc190e7ef31e161b89619458f1"}, - {file = "eth_utils-3.0.0-py3-none-any.whl", hash = "sha256:9a284106acf6f6ce91ddf792489cf8bd4c681fd5ae7653d2f3d5d100be5c3905"}, + {file = "eth_utils-4.1.1-py3-none-any.whl", hash = "sha256:ccbbac68a6d65cb6e294c5bcb6c6a5cec79a241c56dc5d9c345ed788c30f8534"}, + {file = "eth_utils-4.1.1.tar.gz", hash = "sha256:71c8d10dec7494aeed20fa7a4d52ec2ce4a2e52fdce80aab4f5c3c19f3648b25"}, ] [package.dependencies] @@ -1039,54 +1169,79 @@ eth-typing = ">=3.0.0" toolz = {version = ">0.8.2", markers = "implementation_name == \"pypy\""} [package.extras] -dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "hypothesis (>=4.43.0)", "ipython", "mypy (==1.5.1)", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] -docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-hash[pycryptodome]", "hypothesis (>=4.43.0)", "ipython", "mypy (==1.5.1)", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] test = ["hypothesis (>=4.43.0)", "mypy (==1.5.1)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "fastapi" -version = "0.110.0" +version = "0.111.0" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.110.0-py3-none-any.whl", hash = "sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b"}, - {file = "fastapi-0.110.0.tar.gz", hash = "sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3"}, + {file = "fastapi-0.111.0-py3-none-any.whl", hash = "sha256:97ecbf994be0bcbdadedf88c3150252bed7b2087075ac99735403b1b76cc8fc0"}, + {file = "fastapi-0.111.0.tar.gz", hash = "sha256:b9db9dd147c91cb8b769f7183535773d8741dd46f9dc6676cd82eab510228cd7"}, ] [package.dependencies] +email_validator = ">=2.0.0" +fastapi-cli = ">=0.0.2" +httpx = ">=0.23.0" +jinja2 = ">=2.11.2" +orjson = ">=3.2.1" pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.36.3,<0.37.0" +python-multipart = ">=0.0.7" +starlette = ">=0.37.2,<0.38.0" typing-extensions = ">=4.8.0" +ujson = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0" +uvicorn = {version = ">=0.12.0", extras = ["standard"]} + +[package.extras] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-cli" +version = "0.0.4" +description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"}, + {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, +] + +[package.dependencies] +typer = ">=0.12.3" [package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] [[package]] name = "filelock" -version = "3.13.1" +version = "3.14.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] name = "freezegun" -version = "1.4.0" +version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, - {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] @@ -1251,22 +1406,23 @@ test = ["objgraph", "psutil"] [[package]] name = "gunicorn" -version = "21.2.0" +version = "22.0.0" description = "WSGI HTTP Server for UNIX" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" files = [ - {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, - {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, + {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"}, + {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"}, ] [package.dependencies] packaging = "*" [package.extras] -eventlet = ["eventlet (>=0.24.1)"] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] gevent = ["gevent (>=1.4.0)"] setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] tornado = ["tornado (>=0.2)"] [[package]] @@ -1282,30 +1438,29 @@ files = [ [[package]] name = "hexbytes" -version = "0.3.1" +version = "1.2.0" description = "hexbytes: Python `bytes` subclass that decodes hex, with a readable console output" optional = false -python-versions = ">=3.7, <4" +python-versions = ">=3.8, <4" files = [ - {file = "hexbytes-0.3.1-py3-none-any.whl", hash = "sha256:383595ad75026cf00abd570f44b368c6cdac0c6becfae5c39ff88829877f8a59"}, - {file = "hexbytes-0.3.1.tar.gz", hash = "sha256:a3fe35c6831ee8fafd048c4c086b986075fc14fd46258fa24ecb8d65745f9a9d"}, + {file = "hexbytes-1.2.0-py3-none-any.whl", hash = "sha256:bb243ab58b8d8390e3a753fbc9e3616f0f958df43d874e19ae0e4b746722a7e9"}, + {file = "hexbytes-1.2.0.tar.gz", hash = "sha256:965f1cc712e7b263c41fdf3fb36cf671ba6f59b895937cf33941a5c996ec3a5c"}, ] [package.extras] -dev = ["black (>=22)", "bumpversion (>=0.5.3)", "eth-utils (>=1.0.1,<3)", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "hypothesis (>=3.44.24,<=6.31.6)", "ipython", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=5.0.0)", "pytest (>=7.0.0)", "pytest-watch (>=4.1.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] -doc = ["sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] -lint = ["black (>=22)", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=5.0.0)"] -test = ["eth-utils (>=1.0.1,<3)", "hypothesis (>=3.44.24,<=6.31.6)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-utils (>=2.0.0)", "hypothesis (>=3.44.24,<=6.31.6)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +test = ["eth-utils (>=2.0.0)", "hypothesis (>=3.44.24,<=6.31.6)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "httpcore" -version = "1.0.4" +version = "1.0.5" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"}, - {file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"}, + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, ] [package.dependencies] @@ -1316,7 +1471,7 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.25.0)"] +trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httptools" @@ -1395,7 +1550,7 @@ name = "ibet-prime-explorer" version = "0.1.0" description = "ibet-Prime Terminal UI for Block Chain Explorer" optional = true -python-versions = "3.11.2" +python-versions = "3.12.2" files = [] develop = true @@ -1403,15 +1558,28 @@ develop = true type = "directory" url = "cmd/explorer" +[[package]] +name = "ibet-prime-settlement" +version = "0.1.0" +description = "ibet-Prime Settlement CLI" +optional = true +python-versions = "3.12.2" +files = [] +develop = true + +[package.source] +type = "directory" +url = "cmd/settlement" + [[package]] name = "identify" -version = "2.5.35" +version = "2.5.36" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, - {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, ] [package.extras] @@ -1419,24 +1587,24 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] name = "importlib-metadata" -version = "7.0.2" +version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, - {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] @@ -1445,7 +1613,7 @@ zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -1473,51 +1641,33 @@ files = [ colors = ["colorama (>=0.4.6)"] [[package]] -name = "jmespath" -version = "1.0.1" -description = "JSON Matching Expressions" +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, - {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, -] - -[[package]] -name = "jsonschema" -version = "4.21.1" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, - {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.7.1" +MarkupSafe = ">=2.0" [package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] +i18n = ["Babel (>=2.7)"] [[package]] -name = "jsonschema-specifications" -version = "2023.12.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] -[package.dependencies] -referencing = ">=0.31.0" - [[package]] name = "linkify-it-py" version = "2.0.3" @@ -1538,109 +1688,15 @@ dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] doc = ["myst-parser", "sphinx", "sphinx-book-theme"] test = ["coverage", "pytest", "pytest-cov"] -[[package]] -name = "lru-dict" -version = "1.2.0" -description = "An Dict like LRU container." -optional = false -python-versions = "*" -files = [ - {file = "lru-dict-1.2.0.tar.gz", hash = "sha256:13c56782f19d68ddf4d8db0170041192859616514c706b126d0df2ec72a11bd7"}, - {file = "lru_dict-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:de906e5486b5c053d15b7731583c25e3c9147c288ac8152a6d1f9bccdec72641"}, - {file = "lru_dict-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604d07c7604b20b3130405d137cae61579578b0e8377daae4125098feebcb970"}, - {file = "lru_dict-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:203b3e78d03d88f491fa134f85a42919020686b6e6f2d09759b2f5517260c651"}, - {file = "lru_dict-1.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:020b93870f8c7195774cbd94f033b96c14f51c57537969965c3af300331724fe"}, - {file = "lru_dict-1.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1184d91cfebd5d1e659d47f17a60185bbf621635ca56dcdc46c6a1745d25df5c"}, - {file = "lru_dict-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fc42882b554a86e564e0b662da47b8a4b32fa966920bd165e27bb8079a323bc1"}, - {file = "lru_dict-1.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:18ee88ada65bd2ffd483023be0fa1c0a6a051ef666d1cd89e921dcce134149f2"}, - {file = "lru_dict-1.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:756230c22257597b7557eaef7f90484c489e9ba78e5bb6ab5a5bcfb6b03cb075"}, - {file = "lru_dict-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4da599af36618881748b5db457d937955bb2b4800db891647d46767d636c408"}, - {file = "lru_dict-1.2.0-cp310-cp310-win32.whl", hash = "sha256:35a142a7d1a4fd5d5799cc4f8ab2fff50a598d8cee1d1c611f50722b3e27874f"}, - {file = "lru_dict-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:6da5b8099766c4da3bf1ed6e7d7f5eff1681aff6b5987d1258a13bd2ed54f0c9"}, - {file = "lru_dict-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b20b7c9beb481e92e07368ebfaa363ed7ef61e65ffe6e0edbdbaceb33e134124"}, - {file = "lru_dict-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22147367b296be31cc858bf167c448af02435cac44806b228c9be8117f1bfce4"}, - {file = "lru_dict-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a3091abeb95e707f381a8b5b7dc8e4ee016316c659c49b726857b0d6d1bd7a"}, - {file = "lru_dict-1.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:877801a20f05c467126b55338a4e9fa30e2a141eb7b0b740794571b7d619ee11"}, - {file = "lru_dict-1.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d3336e901acec897bcd318c42c2b93d5f1d038e67688f497045fc6bad2c0be7"}, - {file = "lru_dict-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8dafc481d2defb381f19b22cc51837e8a42631e98e34b9e0892245cc96593deb"}, - {file = "lru_dict-1.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:87bbad3f5c3de8897b8c1263a9af73bbb6469fb90e7b57225dad89b8ef62cd8d"}, - {file = "lru_dict-1.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:25f9e0bc2fe8f41c2711ccefd2871f8a5f50a39e6293b68c3dec576112937aad"}, - {file = "lru_dict-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ae301c282a499dc1968dd633cfef8771dd84228ae9d40002a3ea990e4ff0c469"}, - {file = "lru_dict-1.2.0-cp311-cp311-win32.whl", hash = "sha256:c9617583173a29048e11397f165501edc5ae223504a404b2532a212a71ecc9ed"}, - {file = "lru_dict-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6b7a031e47421d4b7aa626b8c91c180a9f037f89e5d0a71c4bb7afcf4036c774"}, - {file = "lru_dict-1.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ea2ac3f7a7a2f32f194c84d82a034e66780057fd908b421becd2f173504d040e"}, - {file = "lru_dict-1.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd46c94966f631a81ffe33eee928db58e9fbee15baba5923d284aeadc0e0fa76"}, - {file = "lru_dict-1.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:086ce993414f0b28530ded7e004c77dc57c5748fa6da488602aa6e7f79e6210e"}, - {file = "lru_dict-1.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df25a426446197488a6702954dcc1de511deee20c9db730499a2aa83fddf0df1"}, - {file = "lru_dict-1.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c53b12b89bd7a6c79f0536ff0d0a84fdf4ab5f6252d94b24b9b753bd9ada2ddf"}, - {file = "lru_dict-1.2.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f9484016e6765bd295708cccc9def49f708ce07ac003808f69efa386633affb9"}, - {file = "lru_dict-1.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d0f7ec902a0097ac39f1922c89be9eaccf00eb87751e28915320b4f72912d057"}, - {file = "lru_dict-1.2.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:981ef3edc82da38d39eb60eae225b88a538d47b90cce2e5808846fd2cf64384b"}, - {file = "lru_dict-1.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e25b2e90a032dc248213af7f3f3e975e1934b204f3b16aeeaeaff27a3b65e128"}, - {file = "lru_dict-1.2.0-cp36-cp36m-win32.whl", hash = "sha256:59f3df78e94e07959f17764e7fa7ca6b54e9296953d2626a112eab08e1beb2db"}, - {file = "lru_dict-1.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:de24b47159e07833aeab517d9cb1c3c5c2d6445cc378b1c2f1d8d15fb4841d63"}, - {file = "lru_dict-1.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d0dd4cd58220351233002f910e35cc01d30337696b55c6578f71318b137770f9"}, - {file = "lru_dict-1.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a87bdc291718bbdf9ea4be12ae7af26cbf0706fa62c2ac332748e3116c5510a7"}, - {file = "lru_dict-1.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05fb8744f91f58479cbe07ed80ada6696ec7df21ea1740891d4107a8dd99a970"}, - {file = "lru_dict-1.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00f6e8a3fc91481b40395316a14c94daa0f0a5de62e7e01a7d589f8d29224052"}, - {file = "lru_dict-1.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b172fce0a0ffc0fa6d282c14256d5a68b5db1e64719c2915e69084c4b6bf555"}, - {file = "lru_dict-1.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e707d93bae8f0a14e6df1ae8b0f076532b35f00e691995f33132d806a88e5c18"}, - {file = "lru_dict-1.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b9ec7a4a0d6b8297102aa56758434fb1fca276a82ed7362e37817407185c3abb"}, - {file = "lru_dict-1.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:f404dcc8172da1f28da9b1f0087009578e608a4899b96d244925c4f463201f2a"}, - {file = "lru_dict-1.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1171ad3bff32aa8086778be4a3bdff595cc2692e78685bcce9cb06b96b22dcc2"}, - {file = "lru_dict-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:0c316dfa3897fabaa1fe08aae89352a3b109e5f88b25529bc01e98ac029bf878"}, - {file = "lru_dict-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5919dd04446bc1ee8d6ecda2187deeebfff5903538ae71083e069bc678599446"}, - {file = "lru_dict-1.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbf36c5a220a85187cacc1fcb7dd87070e04b5fc28df7a43f6842f7c8224a388"}, - {file = "lru_dict-1.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:712e71b64da181e1c0a2eaa76cd860265980cd15cb0e0498602b8aa35d5db9f8"}, - {file = "lru_dict-1.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f54908bf91280a9b8fa6a8c8f3c2f65850ce6acae2852bbe292391628ebca42f"}, - {file = "lru_dict-1.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3838e33710935da2ade1dd404a8b936d571e29268a70ff4ca5ba758abb3850df"}, - {file = "lru_dict-1.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5d5a5f976b39af73324f2b793862859902ccb9542621856d51a5993064f25e4"}, - {file = "lru_dict-1.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8bda3a9afd241ee0181661decaae25e5336ce513ac268ab57da737eacaa7871f"}, - {file = "lru_dict-1.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bd2cd1b998ea4c8c1dad829fc4fa88aeed4dee555b5e03c132fc618e6123f168"}, - {file = "lru_dict-1.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b55753ee23028ba8644fd22e50de7b8f85fa60b562a0fafaad788701d6131ff8"}, - {file = "lru_dict-1.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7e51fa6a203fa91d415f3b2900e5748ec8e06ad75777c98cc3aeb3983ca416d7"}, - {file = "lru_dict-1.2.0-cp38-cp38-win32.whl", hash = "sha256:cd6806313606559e6c7adfa0dbeb30fc5ab625f00958c3d93f84831e7a32b71e"}, - {file = "lru_dict-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d90a70c53b0566084447c3ef9374cc5a9be886e867b36f89495f211baabd322"}, - {file = "lru_dict-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3ea7571b6bf2090a85ff037e6593bbafe1a8598d5c3b4560eb56187bcccb4dc"}, - {file = "lru_dict-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:287c2115a59c1c9ed0d5d8ae7671e594b1206c36ea9df2fca6b17b86c468ff99"}, - {file = "lru_dict-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5ccfd2291c93746a286c87c3f895165b697399969d24c54804ec3ec559d4e43"}, - {file = "lru_dict-1.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b710f0f4d7ec4f9fa89dfde7002f80bcd77de8024017e70706b0911ea086e2ef"}, - {file = "lru_dict-1.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5345bf50e127bd2767e9fd42393635bbc0146eac01f6baf6ef12c332d1a6a329"}, - {file = "lru_dict-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:291d13f85224551913a78fe695cde04cbca9dcb1d84c540167c443eb913603c9"}, - {file = "lru_dict-1.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d5bb41bc74b321789803d45b124fc2145c1b3353b4ad43296d9d1d242574969b"}, - {file = "lru_dict-1.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0facf49b053bf4926d92d8d5a46fe07eecd2af0441add0182c7432d53d6da667"}, - {file = "lru_dict-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:987b73a06bcf5a95d7dc296241c6b1f9bc6cda42586948c9dabf386dc2bef1cd"}, - {file = "lru_dict-1.2.0-cp39-cp39-win32.whl", hash = "sha256:231d7608f029dda42f9610e5723614a35b1fff035a8060cf7d2be19f1711ace8"}, - {file = "lru_dict-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:71da89e134747e20ed5b8ad5b4ee93fc5b31022c2b71e8176e73c5a44699061b"}, - {file = "lru_dict-1.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:21b3090928c7b6cec509e755cc3ab742154b33660a9b433923bd12c37c448e3e"}, - {file = "lru_dict-1.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaecd7085212d0aa4cd855f38b9d61803d6509731138bf798a9594745953245b"}, - {file = "lru_dict-1.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ead83ac59a29d6439ddff46e205ce32f8b7f71a6bd8062347f77e232825e3d0a"}, - {file = "lru_dict-1.2.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:312b6b2a30188586fe71358f0f33e4bac882d33f5e5019b26f084363f42f986f"}, - {file = "lru_dict-1.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b30122e098c80e36d0117810d46459a46313421ce3298709170b687dc1240b02"}, - {file = "lru_dict-1.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f010cfad3ab10676e44dc72a813c968cd586f37b466d27cde73d1f7f1ba158c2"}, - {file = "lru_dict-1.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20f5f411f7751ad9a2c02e80287cedf69ae032edd321fe696e310d32dd30a1f8"}, - {file = "lru_dict-1.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afdadd73304c9befaed02eb42f5f09fdc16288de0a08b32b8080f0f0f6350aa6"}, - {file = "lru_dict-1.2.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7ab0c10c4fa99dc9e26b04e6b62ac32d2bcaea3aad9b81ec8ce9a7aa32b7b1b"}, - {file = "lru_dict-1.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:edad398d5d402c43d2adada390dd83c74e46e020945ff4df801166047013617e"}, - {file = "lru_dict-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:91d577a11b84387013815b1ad0bb6e604558d646003b44c92b3ddf886ad0f879"}, - {file = "lru_dict-1.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb12f19cdf9c4f2d9aa259562e19b188ff34afab28dd9509ff32a3f1c2c29326"}, - {file = "lru_dict-1.2.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e4c85aa8844bdca3c8abac3b7f78da1531c74e9f8b3e4890c6e6d86a5a3f6c0"}, - {file = "lru_dict-1.2.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c6acbd097b15bead4de8e83e8a1030bb4d8257723669097eac643a301a952f0"}, - {file = "lru_dict-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b6613daa851745dd22b860651de930275be9d3e9373283a2164992abacb75b62"}, -] - -[package.extras] -test = ["pytest"] - [[package]] name = "mako" -version = "1.3.2" +version = "1.3.5" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" files = [ - {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, - {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, + {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, + {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, ] [package.dependencies] @@ -1748,13 +1804,13 @@ files = [ [[package]] name = "mdit-py-plugins" -version = "0.4.0" +version = "0.4.1" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ - {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, - {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, + {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, + {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, ] [package.dependencies] @@ -1776,6 +1832,64 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "memray" +version = "1.12.0" +description = "A memory profiler for Python applications" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "memray-1.12.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:27b6ad53081b2f588485393df6498be6d07c2331435625472dc6111b265bef44"}, + {file = "memray-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:64b4e881262c60768ddfbbf062f3861f2bd0cd04414d4ee4cf4f8834285d3d7c"}, + {file = "memray-1.12.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:df79afd83b4c3b5139c7d47ca1d40f5859d7c5be38759a4a698de181414b7cd6"}, + {file = "memray-1.12.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:28f543f5c42491e233dbcb4c87da4a890cef1634b1cd6d0746092d74dc5adcb0"}, + {file = "memray-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a2ad07e0a0bcf15772f7887a1739e1e0679223ee4025493e0011ef35e30aa75"}, + {file = "memray-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:621ceb522614e0076fdec78ad4d4ef34de6eeb62f63bbdd6a27c56d5ef07bde8"}, + {file = "memray-1.12.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:ff94c5471d72e616d9b80f7d5b7839b9de740088f04b2b5a13eb457d571db073"}, + {file = "memray-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21ec3d82f9535dd66bdad2a33aeb46ebc03801b7d9db1f4ca1c96bc96d2c6253"}, + {file = "memray-1.12.0-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7ab5d0d7c566fc5f9bec28276a5219b20e6b5df4cdce16e506d9eda6cbb207e1"}, + {file = "memray-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7252b98ca98e0ce6f95ad81d161f468ce9ada12e66e64e18e8e7c3b95b203035"}, + {file = "memray-1.12.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3abf058226e72caef0d68f2e850954668305934c20d2ccfef89c3eac2ccf7a40"}, + {file = "memray-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236d5fbc2576847d6c40741b61f6fa2fe5b0079011c4d3843bd9fc3d0b86ba7"}, + {file = "memray-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:df5ac32d9e4458af1a8c7d6864095c8dd3bbbad4978fead3dd2d939368b1606b"}, + {file = "memray-1.12.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:22f10a95e74675ce96ae29e968a978e27b9ce466bdcaa322c15edb6ea6ff661a"}, + {file = "memray-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fcfe985ce1b019987258a2edb71edc07a38d96d9c0ab28a91880b1e048b68d8e"}, + {file = "memray-1.12.0-cp312-cp312-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef93e74548d55533f02f7e27417f88e2606f54f5cfd58ed84e0f2d6839e4ad48"}, + {file = "memray-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a60d2a70077f091f51d9bacb387d5f3d9db37a409ab6b071398a5b5eccffe3ec"}, + {file = "memray-1.12.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee72ca364f65f8a74da2c16c17ffb6768331fae9a14bec955932d2a3203c389d"}, + {file = "memray-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8da8a7088b7b434d77ee40c843c5a66c4c10ded711f876122fb661265a950250"}, + {file = "memray-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0a43b7ea24db0469b26dd2da6058f24bd0882fae35eaff9eb3dd572234869771"}, + {file = "memray-1.12.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2be213607228a56ee2e9de598cd4b14aded45e19d404fba1945d2008fd4e0bb6"}, + {file = "memray-1.12.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a7fd3e5f614666faa380a3094ac03793ada9c69239ea4a4716e929efaa7d8ff4"}, + {file = "memray-1.12.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57f04aeba911dd082c7c057e79bb338449621e58ae4e885dde05b5a45d6edcac"}, + {file = "memray-1.12.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dcc2f96f7cdd0a7bbfd86dafad4e76bb6adc4f41560f474185c8722ed6e653d1"}, + {file = "memray-1.12.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:d3367e3748c64797c4986fa543e784067c5de08037621f5f1e628d6fc98cefe5"}, + {file = "memray-1.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dee3eff75b6a47841fb58c909723c140e72915de24a712e1ee4c157c3e1b44af"}, + {file = "memray-1.12.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2bdbcd44e31fe27822b1920814f87c9cc305d731d38c5d32d93f5f9bfa797a9e"}, + {file = "memray-1.12.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:702a502b8c9a2ca64a34605aa32381f94c6443ba6608a6bf877e10f94d342de9"}, + {file = "memray-1.12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8031156adf4a4ab471c5b835bfa773e2d36a3bbf4338236969d8c0b9d76beda"}, + {file = "memray-1.12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8e0dd3e4440c9d64e174cc9663cb6a8ccefa0f45ee967b4de3c31f1d620008e9"}, + {file = "memray-1.12.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:ae2176753be2157235ad766ec662cd98f1d78de2581b30366942c696b9f8da05"}, + {file = "memray-1.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f464861ddce2d4322412f2d408b796027716a623ef67ada9e4fb95300691dd52"}, + {file = "memray-1.12.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e6760685eb69997c8684358f1070c7252929b2b0e0863ed9adf7c684dc6cdf99"}, + {file = "memray-1.12.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2f39ad9fc06ddc6f9e506e05a2e4036a6678447673f9134a1827266485a124b"}, + {file = "memray-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5693b6210c51f1e66ce33a8d6a6fb5bd84c83a3fca644244c7f417e74cc93480"}, + {file = "memray-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bf282a90f0f01ee419d4c184416ae22f7e26450dd80897fb7998633ffde4ffab"}, + {file = "memray-1.12.0.tar.gz", hash = "sha256:3b61c199a60197ae6164a2b44cd828c52de24083ecc49e9ac7d6287686bd68f3"}, +] + +[package.dependencies] +jinja2 = ">=2.9" +rich = ">=11.2.0" +textual = ">=0.41.0" + +[package.extras] +benchmark = ["asv"] +dev = ["Cython", "IPython", "asv", "black", "bump2version", "check-manifest", "flake8", "furo", "greenlet", "ipython", "isort", "mypy", "pytest", "pytest-cov", "pytest-textual-snapshot", "setuptools", "sphinx", "sphinx-argparse", "towncrier"] +docs = ["IPython", "bump2version", "furo", "sphinx", "sphinx-argparse", "towncrier"] +lint = ["black", "check-manifest", "flake8", "isort", "mypy"] +test = ["Cython", "greenlet", "ipython", "pytest", "pytest-cov", "pytest-textual-snapshot", "setuptools"] + [[package]] name = "msgpack" version = "1.0.8" @@ -1953,75 +2067,68 @@ files = [ [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.0" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.0-py2.py3-none-any.whl", hash = "sha256:508ecec98f9f3330b636d4448c0f1a56fc68017c68f1e7857ebc52acf0eb879a"}, + {file = "nodeenv-1.9.0.tar.gz", hash = "sha256:07f144e90dae547bf0d4ee8da0ee42664a42a04e02ed68e06324348dafe4bdb1"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "orjson" -version = "3.9.15" +version = "3.10.3" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.9.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d61f7ce4727a9fa7680cd6f3986b0e2c732639f46a5e0156e550e35258aa313a"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4feeb41882e8aa17634b589533baafdceb387e01e117b1ec65534ec724023d04"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fbbeb3c9b2edb5fd044b2a070f127a0ac456ffd079cb82746fc84af01ef021a4"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66bcc5670e8a6b78f0313bcb74774c8291f6f8aeef10fe70e910b8040f3ab75"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2973474811db7b35c30248d1129c64fd2bdf40d57d84beed2a9a379a6f57d0ab"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fe41b6f72f52d3da4db524c8653e46243c8c92df826ab5ffaece2dba9cccd58"}, - {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4228aace81781cc9d05a3ec3a6d2673a1ad0d8725b4e915f1089803e9efd2b99"}, - {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f7b65bfaf69493c73423ce9db66cfe9138b2f9ef62897486417a8fcb0a92bfe"}, - {file = "orjson-3.9.15-cp310-none-win32.whl", hash = "sha256:2d99e3c4c13a7b0fb3792cc04c2829c9db07838fb6973e578b85c1745e7d0ce7"}, - {file = "orjson-3.9.15-cp310-none-win_amd64.whl", hash = "sha256:b725da33e6e58e4a5d27958568484aa766e825e93aa20c26c91168be58e08cbb"}, - {file = "orjson-3.9.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c8e8fe01e435005d4421f183038fc70ca85d2c1e490f51fb972db92af6e047c2"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87f1097acb569dde17f246faa268759a71a2cb8c96dd392cd25c668b104cad2f"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff0f9913d82e1d1fadbd976424c316fbc4d9c525c81d047bbdd16bd27dd98cfc"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8055ec598605b0077e29652ccfe9372247474375e0e3f5775c91d9434e12d6b1"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6768a327ea1ba44c9114dba5fdda4a214bdb70129065cd0807eb5f010bfcbb5"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12365576039b1a5a47df01aadb353b68223da413e2e7f98c02403061aad34bde"}, - {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:71c6b009d431b3839d7c14c3af86788b3cfac41e969e3e1c22f8a6ea13139404"}, - {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e18668f1bd39e69b7fed19fa7cd1cd110a121ec25439328b5c89934e6d30d357"}, - {file = "orjson-3.9.15-cp311-none-win32.whl", hash = "sha256:62482873e0289cf7313461009bf62ac8b2e54bc6f00c6fabcde785709231a5d7"}, - {file = "orjson-3.9.15-cp311-none-win_amd64.whl", hash = "sha256:b3d336ed75d17c7b1af233a6561cf421dee41d9204aa3cfcc6c9c65cd5bb69a8"}, - {file = "orjson-3.9.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:82425dd5c7bd3adfe4e94c78e27e2fa02971750c2b7ffba648b0f5d5cc016a73"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c51378d4a8255b2e7c1e5cc430644f0939539deddfa77f6fac7b56a9784160a"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae4e06be04dc00618247c4ae3f7c3e561d5bc19ab6941427f6d3722a0875ef7"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcef128f970bb63ecf9a65f7beafd9b55e3aaf0efc271a4154050fc15cdb386e"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b72758f3ffc36ca566ba98a8e7f4f373b6c17c646ff8ad9b21ad10c29186f00d"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c57bc7b946cf2efa67ac55766e41764b66d40cbd9489041e637c1304400494"}, - {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:946c3a1ef25338e78107fba746f299f926db408d34553b4754e90a7de1d44068"}, - {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f256d03957075fcb5923410058982aea85455d035607486ccb847f095442bda"}, - {file = "orjson-3.9.15-cp312-none-win_amd64.whl", hash = "sha256:5bb399e1b49db120653a31463b4a7b27cf2fbfe60469546baf681d1b39f4edf2"}, - {file = "orjson-3.9.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b17f0f14a9c0ba55ff6279a922d1932e24b13fc218a3e968ecdbf791b3682b25"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f6cbd8e6e446fb7e4ed5bac4661a29e43f38aeecbf60c4b900b825a353276a1"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76bc6356d07c1d9f4b782813094d0caf1703b729d876ab6a676f3aaa9a47e37c"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdfa97090e2d6f73dced247a2f2d8004ac6449df6568f30e7fa1a045767c69a6"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7413070a3e927e4207d00bd65f42d1b780fb0d32d7b1d951f6dc6ade318e1b5a"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cf1596680ac1f01839dba32d496136bdd5d8ffb858c280fa82bbfeb173bdd40"}, - {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:809d653c155e2cc4fd39ad69c08fdff7f4016c355ae4b88905219d3579e31eb7"}, - {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:920fa5a0c5175ab14b9c78f6f820b75804fb4984423ee4c4f1e6d748f8b22bc1"}, - {file = "orjson-3.9.15-cp38-none-win32.whl", hash = "sha256:2b5c0f532905e60cf22a511120e3719b85d9c25d0e1c2a8abb20c4dede3b05a5"}, - {file = "orjson-3.9.15-cp38-none-win_amd64.whl", hash = "sha256:67384f588f7f8daf040114337d34a5188346e3fae6c38b6a19a2fe8c663a2f9b"}, - {file = "orjson-3.9.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6fc2fe4647927070df3d93f561d7e588a38865ea0040027662e3e541d592811e"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34cbcd216e7af5270f2ffa63a963346845eb71e174ea530867b7443892d77180"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f541587f5c558abd93cb0de491ce99a9ef8d1ae29dd6ab4dbb5a13281ae04cbd"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92255879280ef9c3c0bcb327c5a1b8ed694c290d61a6a532458264f887f052cb"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a1f57fb601c426635fcae9ddbe90dfc1ed42245eb4c75e4960440cac667262"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede0bde16cc6e9b96633df1631fbcd66491d1063667f260a4f2386a098393790"}, - {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e88b97ef13910e5f87bcbc4dd7979a7de9ba8702b54d3204ac587e83639c0c2b"}, - {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57d5d8cf9c27f7ef6bc56a5925c7fbc76b61288ab674eb352c26ac780caa5b10"}, - {file = "orjson-3.9.15-cp39-none-win32.whl", hash = "sha256:001f4eb0ecd8e9ebd295722d0cbedf0748680fb9998d3993abaed2f40587257a"}, - {file = "orjson-3.9.15-cp39-none-win_amd64.whl", hash = "sha256:ea0b183a5fe6b2b45f3b854b0d19c4e932d6f5934ae1f723b07cf9560edd4ec7"}, - {file = "orjson-3.9.15.tar.gz", hash = "sha256:95cae920959d772f30ab36d3b25f83bb0f3be671e986c72ce22f8fa700dae061"}, + {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"}, + {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"}, + {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"}, + {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"}, + {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"}, + {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"}, + {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"}, + {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"}, + {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"}, + {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"}, + {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"}, + {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"}, + {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"}, + {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"}, + {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"}, + {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"}, ] [[package]] @@ -2037,12 +2144,13 @@ files = [ [[package]] name = "parsimonious" -version = "0.9.0" +version = "0.10.0" description = "(Soon to be) the fastest pure-Python PEG parser I could muster" optional = false python-versions = "*" files = [ - {file = "parsimonious-0.9.0.tar.gz", hash = "sha256:b2ad1ae63a2f65bd78f5e0a8ac510a98f3607a43f1db2a8d46636a5d9e4a30c1"}, + {file = "parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f"}, + {file = "parsimonious-0.10.0.tar.gz", hash = "sha256:8281600da180ec8ae35427a4ab4f7b82bfec1e3d1e52f80cb60ea82b9512501c"}, ] [package.dependencies] @@ -2061,28 +2169,29 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -2091,13 +2200,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.6.2" +version = "3.7.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.6.2-py2.py3-none-any.whl", hash = "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c"}, - {file = "pre_commit-3.6.2.tar.gz", hash = "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e"}, + {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, + {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, ] [package.dependencies] @@ -2107,45 +2216,25 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" -[[package]] -name = "protobuf" -version = "4.25.3" -description = "" -optional = false -python-versions = ">=3.8" -files = [ - {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, - {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, - {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, - {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, - {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, - {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, - {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, - {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, - {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, -] - [[package]] name = "psycopg" -version = "3.1.18" +version = "3.1.19" description = "PostgreSQL database adapter for Python" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg-3.1.18-py3-none-any.whl", hash = "sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e"}, - {file = "psycopg-3.1.18.tar.gz", hash = "sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b"}, + {file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"}, + {file = "psycopg-3.1.19.tar.gz", hash = "sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961"}, ] [package.dependencies] -psycopg-c = {version = "3.1.18", optional = true, markers = "implementation_name != \"pypy\" and extra == \"c\""} +psycopg-c = {version = "3.1.19", optional = true, markers = "implementation_name != \"pypy\" and extra == \"c\""} typing-extensions = ">=4.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.1.18)"] -c = ["psycopg-c (==3.1.18)"] +binary = ["psycopg-binary (==3.1.19)"] +c = ["psycopg-c (==3.1.19)"] dev = ["black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] @@ -2153,23 +2242,23 @@ test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6 [[package]] name = "psycopg-c" -version = "3.1.18" +version = "3.1.19" description = "PostgreSQL database adapter for Python -- C optimisation distribution" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg-c-3.1.18.tar.gz", hash = "sha256:ffff0c4a9c0e0b7aadb1acb7b61eb8f886365dd8ef00120ce14676235846ba73"}, + {file = "psycopg_c-3.1.19.tar.gz", hash = "sha256:8e90f53c430e7d661cb3a9298e2761847212ead1b24c5fb058fc9d0fd9616017"}, ] [[package]] name = "pycparser" -version = "2.21" +version = "2.22" description = "C parser in Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] [[package]] @@ -2215,18 +2304,18 @@ files = [ [[package]] name = "pydantic" -version = "2.6.3" +version = "2.7.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, - {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, + {file = "pydantic-2.7.2-py3-none-any.whl", hash = "sha256:834ab954175f94e6e68258537dc49402c4a5e9d0409b9f1b86b7e934a8372de7"}, + {file = "pydantic-2.7.2.tar.gz", hash = "sha256:71b2945998f9c9b7919a45bde9a50397b289937d215ae141c1d0903ba7149fd7"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.16.3" +pydantic-core = "2.18.3" typing-extensions = ">=4.6.1" [package.extras] @@ -2234,90 +2323,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.16.3" -description = "" +version = "2.18.3" +description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, - {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, - {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, - {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, - {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, - {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, - {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, - {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, - {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, - {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, - {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, - {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, - {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, - {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, + {file = "pydantic_core-2.18.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:744697428fcdec6be5670460b578161d1ffe34743a5c15656be7ea82b008197c"}, + {file = "pydantic_core-2.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b40c05ced1ba4218b14986fe6f283d22e1ae2ff4c8e28881a70fb81fbfcda7"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a9a75622357076efb6b311983ff190fbfb3c12fc3a853122b34d3d358126c"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2e253af04ceaebde8eb201eb3f3e3e7e390f2d275a88300d6a1959d710539e2"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:855ec66589c68aa367d989da5c4755bb74ee92ccad4fdb6af942c3612c067e34"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3e42bb54e7e9d72c13ce112e02eb1b3b55681ee948d748842171201a03a98a"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6ac9ffccc9d2e69d9fba841441d4259cb668ac180e51b30d3632cd7abca2b9b"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c56eca1686539fa0c9bda992e7bd6a37583f20083c37590413381acfc5f192d6"}, + {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17954d784bf8abfc0ec2a633108207ebc4fa2df1a0e4c0c3ccbaa9bb01d2c426"}, + {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:98ed737567d8f2ecd54f7c8d4f8572ca7c7921ede93a2e52939416170d357812"}, + {file = "pydantic_core-2.18.3-cp310-none-win32.whl", hash = "sha256:9f9e04afebd3ed8c15d67a564ed0a34b54e52136c6d40d14c5547b238390e779"}, + {file = "pydantic_core-2.18.3-cp310-none-win_amd64.whl", hash = "sha256:45e4ffbae34f7ae30d0047697e724e534a7ec0a82ef9994b7913a412c21462a0"}, + {file = "pydantic_core-2.18.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9ebe8231726c49518b16b237b9fe0d7d361dd221302af511a83d4ada01183ab"}, + {file = "pydantic_core-2.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8e20e15d18bf7dbb453be78a2d858f946f5cdf06c5072453dace00ab652e2b2"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0d9ff283cd3459fa0bf9b0256a2b6f01ac1ff9ffb034e24457b9035f75587cb"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f7ef5f0ebb77ba24c9970da18b771711edc5feaf00c10b18461e0f5f5949231"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73038d66614d2e5cde30435b5afdced2b473b4c77d4ca3a8624dd3e41a9c19be"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6afd5c867a74c4d314c557b5ea9520183fadfbd1df4c2d6e09fd0d990ce412cd"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd7df92f28d351bb9f12470f4c533cf03d1b52ec5a6e5c58c65b183055a60106"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:80aea0ffeb1049336043d07799eace1c9602519fb3192916ff525b0287b2b1e4"}, + {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaee40f25bba38132e655ffa3d1998a6d576ba7cf81deff8bfa189fb43fd2bbe"}, + {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9128089da8f4fe73f7a91973895ebf2502539d627891a14034e45fb9e707e26d"}, + {file = "pydantic_core-2.18.3-cp311-none-win32.whl", hash = "sha256:fec02527e1e03257aa25b1a4dcbe697b40a22f1229f5d026503e8b7ff6d2eda7"}, + {file = "pydantic_core-2.18.3-cp311-none-win_amd64.whl", hash = "sha256:58ff8631dbab6c7c982e6425da8347108449321f61fe427c52ddfadd66642af7"}, + {file = "pydantic_core-2.18.3-cp311-none-win_arm64.whl", hash = "sha256:3fc1c7f67f34c6c2ef9c213e0f2a351797cda98249d9ca56a70ce4ebcaba45f4"}, + {file = "pydantic_core-2.18.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f0928cde2ae416a2d1ebe6dee324709c6f73e93494d8c7aea92df99aab1fc40f"}, + {file = "pydantic_core-2.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bee9bb305a562f8b9271855afb6ce00223f545de3d68560b3c1649c7c5295e9"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e862823be114387257dacbfa7d78547165a85d7add33b446ca4f4fae92c7ff5c"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a36f78674cbddc165abab0df961b5f96b14461d05feec5e1f78da58808b97e7"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba905d184f62e7ddbb7a5a751d8a5c805463511c7b08d1aca4a3e8c11f2e5048"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fdd362f6a586e681ff86550b2379e532fee63c52def1c666887956748eaa326"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b214b7ee3bd3b865e963dbed0f8bc5375f49449d70e8d407b567af3222aae4"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691018785779766127f531674fa82bb368df5b36b461622b12e176c18e119022"}, + {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:60e4c625e6f7155d7d0dcac151edf5858102bc61bf959d04469ca6ee4e8381bd"}, + {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4e651e47d981c1b701dcc74ab8fec5a60a5b004650416b4abbef13db23bc7be"}, + {file = "pydantic_core-2.18.3-cp312-none-win32.whl", hash = "sha256:ffecbb5edb7f5ffae13599aec33b735e9e4c7676ca1633c60f2c606beb17efc5"}, + {file = "pydantic_core-2.18.3-cp312-none-win_amd64.whl", hash = "sha256:2c8333f6e934733483c7eddffdb094c143b9463d2af7e6bd85ebcb2d4a1b82c6"}, + {file = "pydantic_core-2.18.3-cp312-none-win_arm64.whl", hash = "sha256:7a20dded653e516a4655f4c98e97ccafb13753987434fe7cf044aa25f5b7d417"}, + {file = "pydantic_core-2.18.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:eecf63195be644b0396f972c82598cd15693550f0ff236dcf7ab92e2eb6d3522"}, + {file = "pydantic_core-2.18.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c44efdd3b6125419c28821590d7ec891c9cb0dff33a7a78d9d5c8b6f66b9702"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e59fca51ffbdd1638b3856779342ed69bcecb8484c1d4b8bdb237d0eb5a45e2"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70cf099197d6b98953468461d753563b28e73cf1eade2ffe069675d2657ed1d5"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63081a49dddc6124754b32a3774331467bfc3d2bd5ff8f10df36a95602560361"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370059b7883485c9edb9655355ff46d912f4b03b009d929220d9294c7fd9fd60"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a64faeedfd8254f05f5cf6fc755023a7e1606af3959cfc1a9285744cc711044"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19d2e725de0f90d8671f89e420d36c3dd97639b98145e42fcc0e1f6d492a46dc"}, + {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:67bc078025d70ec5aefe6200ef094576c9d86bd36982df1301c758a9fff7d7f4"}, + {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:adf952c3f4100e203cbaf8e0c907c835d3e28f9041474e52b651761dc248a3c0"}, + {file = "pydantic_core-2.18.3-cp38-none-win32.whl", hash = "sha256:9a46795b1f3beb167eaee91736d5d17ac3a994bf2215a996aed825a45f897558"}, + {file = "pydantic_core-2.18.3-cp38-none-win_amd64.whl", hash = "sha256:200ad4e3133cb99ed82342a101a5abf3d924722e71cd581cc113fe828f727fbc"}, + {file = "pydantic_core-2.18.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:304378b7bf92206036c8ddd83a2ba7b7d1a5b425acafff637172a3aa72ad7083"}, + {file = "pydantic_core-2.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c826870b277143e701c9ccf34ebc33ddb4d072612683a044e7cce2d52f6c3fef"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e201935d282707394f3668380e41ccf25b5794d1b131cdd96b07f615a33ca4b1"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5560dda746c44b48bf82b3d191d74fe8efc5686a9ef18e69bdabccbbb9ad9442"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b32c2a1f8032570842257e4c19288eba9a2bba4712af542327de9a1204faff8"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:929c24e9dea3990bc8bcd27c5f2d3916c0c86f5511d2caa69e0d5290115344a9"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a8376fef60790152564b0eab376b3e23dd6e54f29d84aad46f7b264ecca943"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dccf3ef1400390ddd1fb55bf0632209d39140552d068ee5ac45553b556780e06"}, + {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41dbdcb0c7252b58fa931fec47937edb422c9cb22528f41cb8963665c372caf6"}, + {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:666e45cf071669fde468886654742fa10b0e74cd0fa0430a46ba6056b24fb0af"}, + {file = "pydantic_core-2.18.3-cp39-none-win32.whl", hash = "sha256:f9c08cabff68704a1b4667d33f534d544b8a07b8e5d039c37067fceb18789e78"}, + {file = "pydantic_core-2.18.3-cp39-none-win_amd64.whl", hash = "sha256:4afa5f5973e8572b5c0dcb4e2d4fda7890e7cd63329bd5cc3263a25c92ef0026"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:77319771a026f7c7d29c6ebc623de889e9563b7087911b46fd06c044a12aa5e9"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:df11fa992e9f576473038510d66dd305bcd51d7dd508c163a8c8fe148454e059"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d531076bdfb65af593326ffd567e6ab3da145020dafb9187a1d131064a55f97c"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33ce258e4e6e6038f2b9e8b8a631d17d017567db43483314993b3ca345dcbbb"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f9cd7f5635b719939019be9bda47ecb56e165e51dd26c9a217a433e3d0d59a9"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cd4a032bb65cc132cae1fe3e52877daecc2097965cd3914e44fbd12b00dae7c5"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f2718430098bcdf60402136c845e4126a189959d103900ebabb6774a5d9fdb"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0037a92cf0c580ed14e10953cdd26528e8796307bb8bb312dc65f71547df04d"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b95a0972fac2b1ff3c94629fc9081b16371dad870959f1408cc33b2f78ad347a"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a62e437d687cc148381bdd5f51e3e81f5b20a735c55f690c5be94e05da2b0d5c"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b367a73a414bbb08507da102dc2cde0fa7afe57d09b3240ce82a16d608a7679c"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ecce4b2360aa3f008da3327d652e74a0e743908eac306198b47e1c58b03dd2b"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4435b8d83f0c9561a2a9585b1de78f1abb17cb0cef5f39bf6a4b47d19bafe3"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:616221a6d473c5b9aa83fa8982745441f6a4a62a66436be9445c65f241b86c94"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7e6382ce89a92bc1d0c0c5edd51e931432202b9080dc921d8d003e616402efd1"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff58f379345603d940e461eae474b6bbb6dab66ed9a851ecd3cb3709bf4dcf6a"}, + {file = "pydantic_core-2.18.3.tar.gz", hash = "sha256:432e999088d85c8f36b9a3f769a8e2b57aabd817bbb729a90d1fe7f18f6f1f39"}, ] [package.dependencies] @@ -2325,17 +2414,16 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] @@ -2444,6 +2532,26 @@ files = [ freezegun = ">=1.0" pytest = ">=3.6" +[[package]] +name = "pytest-memray" +version = "1.6.0" +description = "A simple plugin to use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_memray-1.6.0-py3-none-any.whl", hash = "sha256:267db3f9d3ad3e443c6743e5261ce64caff3e2e8d632850b58c0ae0925fed765"}, + {file = "pytest_memray-1.6.0.tar.gz", hash = "sha256:364152252afd563fc8b58459325f360030d2b0d5673896018a68badb35402339"}, +] + +[package.dependencies] +memray = ">=1.12" +pytest = ">=7.2" + +[package.extras] +docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinx-inline-tabs (>=2022.1.2b11)", "sphinxcontrib-programoutput (>=0.17)", "towncrier (>=22.12)"] +lint = ["black (==22.12)", "isort (==5.11.4)", "mypy (==0.991)", "ruff (==0.0.272)"] +test = ["covdefaults (>=2.2.2)", "coverage (>=7.0.5)", "flaky (>=3.7)", "pytest (>=7.2)", "pytest-xdist (>=3.1)"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -2472,6 +2580,20 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + [[package]] name = "pytz" version = "2024.1" @@ -2565,132 +2687,103 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] -[[package]] -name = "referencing" -version = "0.33.0" -description = "JSON Referencing + Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "referencing-0.33.0-py3-none-any.whl", hash = "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5"}, - {file = "referencing-0.33.0.tar.gz", hash = "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" - [[package]] name = "regex" -version = "2023.12.25" +version = "2024.5.15" description = "Alternative regular expression module, to replace re." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, - {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, - {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, - {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, - {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, - {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, - {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, - {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, - {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, - {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, - {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, - {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, - {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, - {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, - {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53"}, + {file = "regex-2024.5.15-cp310-cp310-win32.whl", hash = "sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3"}, + {file = "regex-2024.5.15-cp310-cp310-win_amd64.whl", hash = "sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68"}, + {file = "regex-2024.5.15-cp311-cp311-win32.whl", hash = "sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa"}, + {file = "regex-2024.5.15-cp311-cp311-win_amd64.whl", hash = "sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80"}, + {file = "regex-2024.5.15-cp312-cp312-win32.whl", hash = "sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe"}, + {file = "regex-2024.5.15-cp312-cp312-win_amd64.whl", hash = "sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741"}, + {file = "regex-2024.5.15-cp38-cp38-win32.whl", hash = "sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9"}, + {file = "regex-2024.5.15-cp38-cp38-win_amd64.whl", hash = "sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456"}, + {file = "regex-2024.5.15-cp39-cp39-win32.whl", hash = "sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694"}, + {file = "regex-2024.5.15-cp39-cp39-win_amd64.whl", hash = "sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388"}, + {file = "regex-2024.5.15.tar.gz", hash = "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c"}, ] [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -2723,141 +2816,110 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rlp" -version = "4.0.0" +version = "4.0.1" description = "rlp: A package for Recursive Length Prefix encoding and decoding" optional = false -python-versions = ">=3.8, <4" +python-versions = "<4,>=3.8" files = [ - {file = "rlp-4.0.0-py3-none-any.whl", hash = "sha256:1747fd933e054e6d25abfe591be92e19a4193a56c93981c05bd0f84dfe279f14"}, - {file = "rlp-4.0.0.tar.gz", hash = "sha256:61a5541f86e4684ab145cb849a5929d2ced8222930a570b3941cf4af16b72a78"}, + {file = "rlp-4.0.1-py3-none-any.whl", hash = "sha256:ff6846c3c27b97ee0492373aa074a7c3046aadd973320f4fffa7ac45564b0258"}, + {file = "rlp-4.0.1.tar.gz", hash = "sha256:bcefb11013dfadf8902642337923bd0c786dc8a27cb4c21da6e154e52869ecb1"}, ] [package.dependencies] eth-utils = ">=2" [package.extras] -dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "hypothesis (==5.19.0)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] -docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] -rust-backend = ["rusty-rlp (>=0.2.1,<0.3)"] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "hypothesis (==5.19.0)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +rust-backend = ["rusty-rlp (>=0.2.1)"] test = ["hypothesis (==5.19.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] -name = "rpds-py" -version = "0.18.0" -description = "Python bindings to Rust's persistent data structures (rpds)" +name = "ruamel-yaml" +version = "0.18.6" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" +files = [ + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.6" files = [ - {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, - {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, - {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, - {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, - {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, - {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, - {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, - {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, - {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, - {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, - {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, - {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, - {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, ] [[package]] name = "s3transfer" -version = "0.10.0" +version = "0.10.1" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">= 3.8" files = [ - {file = "s3transfer-0.10.0-py3-none-any.whl", hash = "sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e"}, - {file = "s3transfer-0.10.0.tar.gz", hash = "sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b"}, + {file = "s3transfer-0.10.1-py3-none-any.whl", hash = "sha256:ceb252b11bcf87080fb7850a224fb6e05c8a776bab8f2b64b7f25b969464839d"}, + {file = "s3transfer-0.10.1.tar.gz", hash = "sha256:5683916b4c724f799e600f41dd9e10a9ff19871bf87623cc8f491cb4f5fa0a19"}, ] [package.dependencies] @@ -2866,22 +2928,6 @@ botocore = ">=1.33.2,<2.0a.0" [package.extras] crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] -[[package]] -name = "setuptools" -version = "69.1.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, - {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - [[package]] name = "shared-memory-dict" version = "0.7.2" @@ -2898,6 +2944,17 @@ aiocache = ["aiocache (>=0.11.1,<0.12.0)"] all = ["aiocache (>=0.11.1,<0.12.0)", "django (>=3.0.8,<4.0.0)"] django = ["django (>=3.0.8,<4.0.0)"] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + [[package]] name = "six" version = "1.16.0" @@ -2922,60 +2979,60 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.28" +version = "2.0.30" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, - {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, - {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, + {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, + {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"}, + {file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"}, + {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"}, + {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"}, + {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, + {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, ] [package.dependencies] @@ -3009,13 +3066,13 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "starlette" -version = "0.36.3" +version = "0.37.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"}, - {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, ] [package.dependencies] @@ -3075,33 +3132,30 @@ files = [ [[package]] name = "typer" -version = "0.7.0" +version = "0.12.3" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = true -python-versions = ">=3.6" +optional = false +python-versions = ">=3.7" files = [ - {file = "typer-0.7.0-py3-none-any.whl", hash = "sha256:b5e704f4e48ec263de1c0b3a2387cd405a13767d2f907f44c1a08cbad96f606d"}, - {file = "typer-0.7.0.tar.gz", hash = "sha256:ff797846578a9f2a201b53442aedeb543319466870fbe1c701eab66dd7681165"}, + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, ] [package.dependencies] -click = ">=7.1.1,<9.0.0" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.12.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, ] [[package]] @@ -3129,32 +3183,119 @@ files = [ [package.extras] test = ["coverage", "pytest", "pytest-cov"] +[[package]] +name = "ujson" +version = "5.10.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, + {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, + {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, + {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, + {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, + {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, + {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, + {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, + {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, + {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, + {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, + {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, + {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, + {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, +] + [[package]] name = "urllib3" -version = "2.0.7" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, - {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.27.1" +version = "0.30.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"}, - {file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"}, + {file = "uvicorn-0.30.0-py3-none-any.whl", hash = "sha256:78fa0b5f56abb8562024a59041caeb555c86e48d0efdd23c3fe7de7a4075bdab"}, + {file = "uvicorn-0.30.0.tar.gz", hash = "sha256:f678dec4fa3a39706bbf49b9ec5fc40049d42418716cea52b53f07828a60aa37"}, ] [package.dependencies] @@ -3217,13 +3358,13 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.25.1" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, - {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] @@ -3232,91 +3373,91 @@ filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "watchfiles" -version = "0.21.0" +version = "0.22.0" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.8" files = [ - {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, - {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, - {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, - {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, - {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, - {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, - {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, - {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, - {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, - {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, - {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, - {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, - {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, - {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, - {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, + {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, + {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, + {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, + {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, + {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, + {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, + {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, + {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, + {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, + {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, + {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, + {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, + {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, ] [package.dependencies] @@ -3324,26 +3465,24 @@ anyio = ">=3.0.0" [[package]] name = "web3" -version = "6.15.1" -description = "web3.py" +version = "7.0.0b6" +description = "web3: A Python library for interacting with Ethereum" optional = false -python-versions = ">=3.7.2" +python-versions = "<4,>=3.8" files = [ - {file = "web3-6.15.1-py3-none-any.whl", hash = "sha256:4e4a8313aa4556ecde61c852a62405b853b667498b07da6ff05c29fe8c79096b"}, - {file = "web3-6.15.1.tar.gz", hash = "sha256:f9e7eefc1b3c3d194868a4ef9583b625c18ea3f31a48ebe143183db74898f381"}, + {file = "web3-7.0.0b6-py3-none-any.whl", hash = "sha256:96c424f93df2d543d64cbe1758d2a95191e925579f503023d4fd6479d449a5ed"}, + {file = "web3-7.0.0b6.tar.gz", hash = "sha256:2e33398652c274f35e2cd432327a58a03cd233bb0721c11e4b95687a6faccd2f"}, ] [package.dependencies] aiohttp = ">=3.7.4.post0" -eth-abi = ">=4.0.0" -eth-account = ">=0.8.0" +eth-abi = ">=5.0.1" +eth-account = ">=0.12.2" eth-hash = {version = ">=0.5.1", extras = ["pycryptodome"]} -eth-typing = ">=3.0.0" -eth-utils = ">=2.1.0" -hexbytes = ">=0.1.0,<0.4.0" -jsonschema = ">=4.0.0" -lru-dict = ">=1.1.6,<1.3.0" -protobuf = ">=4.21.6" +eth-typing = ">=4.0.0" +eth-utils = ">=4.0.0" +hexbytes = ">=1.2.0" +pydantic = ">=2.4.0" pyunormalize = ">=15.0.0" pywin32 = {version = ">=223", markers = "platform_system == \"Windows\""} requests = ">=2.16.0" @@ -3351,11 +3490,9 @@ typing-extensions = ">=4.0.1" websockets = ">=10.0.0" [package.extras] -dev = ["black (>=22.1.0)", "build (>=0.9.0)", "bumpversion", "eth-tester[py-evm] (==v0.9.1-b.2)", "flake8 (==3.8.3)", "flaky (>=3.7.0)", "hypothesis (>=3.31.2)", "importlib-metadata (<5.0)", "ipfshttpclient (==0.8.0a2)", "isort (>=5.11.0)", "mypy (==1.4.1)", "py-geth (>=3.14.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.18.1,<0.23)", "pytest-mock (>=1.10)", "pytest-watch (>=4.2)", "pytest-xdist (>=1.29)", "setuptools (>=38.6.0)", "sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=3.18.0)", "tqdm (>4.32)", "twine (>=1.13)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)", "when-changed (>=0.3.0)"] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] -ipfs = ["ipfshttpclient (==0.8.0a2)"] -linter = ["black (>=22.1.0)", "flake8 (==3.8.3)", "isort (>=5.11.0)", "mypy (==1.4.1)", "types-protobuf (==3.19.13)", "types-requests (>=2.26.1)", "types-setuptools (>=57.4.4)"] -tester = ["eth-tester[py-evm] (==v0.9.1-b.2)", "py-geth (>=3.14.0)"] +dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "eth-tester[py-evm] (>=0.11.0b1,<0.12.0b1)", "flaky (>=3.7.0)", "hypothesis (>=3.31.2)", "ipython", "pre-commit (>=3.4.0)", "py-geth (>=4.1.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.18.1,<0.23)", "pytest-asyncio (>=0.21.2,<0.23)", "pytest-mock (>=1.10)", "pytest-xdist (>=2.4.0)", "setuptools (>=38.6.0)", "sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "tqdm (>4.32)", "twine (>=1.13)", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +test = ["eth-tester[py-evm] (>=0.11.0b1,<0.12.0b1)", "py-geth (>=4.1.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.18.1,<0.23)", "pytest-mock (>=1.10)", "pytest-xdist (>=2.4.0)"] [[package]] name = "websockets" @@ -3543,23 +3680,24 @@ multidict = ">=4.0" [[package]] name = "zipp" -version = "3.17.0" +version = "3.19.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.19.0-py3-none-any.whl", hash = "sha256:96dc6ad62f1441bcaccef23b274ec471518daf4fbbc580341204936a5a3dddec"}, + {file = "zipp-3.19.0.tar.gz", hash = "sha256:952df858fb3164426c976d9338d3961e8e8b3758e2e059e0f754b8c4262625ee"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [extras] ibet-explorer = ["aiohttp", "async-cache", "ibet-prime-explorer", "textual", "typer"] +settlement-cli = ["ibet-prime-settlement", "typer"] [metadata] lock-version = "2.0" -python-versions = "3.11.2" -content-hash = "832f492b0a4bc89eaf808bdf6080d50ff5dcb6a6fbb9bdf4cda00d2aedc7e05d" +python-versions = "3.12.2" +content-hash = "a3aa9da265176a0f230087f3eadd2880fcf610df97ff9915ed0f7a688f5f133c" diff --git a/pyproject.toml b/pyproject.toml index f55a6c68..04318d00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,36 +1,42 @@ [tool.poetry] name = "ibet-prime" -version = "24.3" +version = "24.6" description = "ibet-Prime is an API service that enables the issuance and management of security tokens on the ibet network." authors = ["BOOSTRY Co., Ltd. "] license = "Apache License, Version 2.0" readme = "README.md" +[tool.poetry.scripts] +ibet-explorer = "cmd.explorer.src:main" +settlement-cli = "cmd.settlement.src:main" + [tool.poetry.dependencies] -python = "3.11.2" +python = "3.12.2" alembic = "^1.13.1" boto3 = "~1.34.59" coincurve = "~19.0.1" -eth-keyfile = "0.7.0" -eth-utils = "~3.0.0" -fastapi = "~0.110.0" -gunicorn = "~21.2.0" -orjson = "~3.9.15" +eth-keyfile = "0.8.1" +eth-utils = "~4.1.1" +fastapi = "~0.111.0" +gunicorn = "~22.0.0" +orjson = "~3.10.3" psycopg = {extras = ["c"], version = "^3.1.18"} pycryptodome = "~3.20" -pydantic = "~2.6.3" +pydantic = "~2.7.1" pytz = "~2024.1" shared-memory-dict = "~0.7.2" -web3 = "~6.15.1" +web3 = "7.0.0b6" httpx = "^0.27.0" sqlalchemy = {extras = ["asyncio"], version = "^2.0.28"} -uvicorn = {extras = ["standard"], version = "~0.27.0"} +uvicorn = {extras = ["standard"], version = "~0.30.0"} uvloop = "~0.19.0" +memray = "^1.12.0" ibet-prime-explorer = {path = "cmd/explorer", optional = true, develop = true} +ibet-prime-settlement = {path = "cmd/settlement", optional = true, develop = true} textual = {version = "~0.44.1", optional = true} async-cache = {version = "~1.1.1", optional = true} -typer = {version = "~0.7.0", optional = true} +typer = "0.12.3" aiohttp = {version = "~3.9.3", optional = true} [tool.poetry.group.dev.dependencies] @@ -39,13 +45,14 @@ pytest-cov = "^4.1.0" pyyaml = "^6.0" pre-commit = "^3.6.0" isort = "^5.13.2" -httpx = "^0.27.0" black = "^24.1.1" textual-dev = "^1.2.1" pytest-alembic = "^0.10.7" pytest-freezer = "^0.4.8" pytest-asyncio = "0.23.3" pytest-aiohttp = "^1.0.5" +ruamel-yaml = "^0.18.6" +pytest-memray = "^1.6.0" [tool.poetry.extras] ibet-explorer = [ @@ -55,6 +62,10 @@ ibet-explorer = [ "typer", "aiohttp" ] +settlement-cli = [ + "ibet-prime-settlement", + "typer", +] [tool.isort] profile = "black" diff --git a/tests/Dockerfile_hardhat b/tests/Dockerfile_hardhat new file mode 100644 index 00000000..043ebf19 --- /dev/null +++ b/tests/Dockerfile_hardhat @@ -0,0 +1,17 @@ +FROM node:20-alpine + +# Make directory +RUN mkdir -p /hardhat + +# Copy files +COPY package.json /hardhat +COPY package-lock.json /hardhat +COPY hardhat.config.js /hardhat + +WORKDIR /hardhat + +# Install hardhat +RUN npm install --only=dev + +# Run hardhat node +CMD ["npx", "hardhat", "--config", "./hardhat.config.js", "node"] \ No newline at end of file diff --git a/tests/Dockerfile b/tests/Dockerfile_unittest similarity index 90% rename from tests/Dockerfile rename to tests/Dockerfile_unittest index 391aa407..224711ae 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile_unittest @@ -52,15 +52,16 @@ RUN echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~apl/.bash_profile \ # install python USER apl RUN . ~/.bash_profile \ - && pyenv install 3.11.2 \ - && pyenv global 3.11.2 \ + && pyenv install 3.12.2 \ + && pyenv global 3.12.2 \ && pip install --upgrade pip setuptools # install poetry RUN . ~/.bash_profile \ - && python -m pip install poetry==1.7.1 + && python -m pip install poetry==1.8.2 RUN . ~/.bash_profile \ - && poetry config virtualenvs.create false + && poetry config virtualenvs.create false \ + && poetry config installer.max-workers 1 # install python packages USER root @@ -88,7 +89,7 @@ COPY pyproject.toml /app/ibet-Prime/pyproject.toml COPY poetry.lock /app/ibet-Prime/poetry.lock RUN . ~/.bash_profile \ && cd /app/ibet-Prime \ - && poetry install --no-root -E ibet-explorer \ + && poetry install --no-root --all-extras \ && rm -f /app/ibet-Prime/pyproject.toml \ && rm -f /app/ibet-Prime/poetry.lock @@ -97,7 +98,7 @@ USER root RUN mkdir -p /app/ibet-Prime/tests/ COPY --chown=apl:apl tests/ /app/ibet-Prime/tests/ RUN chmod -R 755 /app/ibet-Prime/tests/ -ENV PYTHONPATH /app/ibet-Prime +ENV PYTHONPATH /app/ibet-Prime:/app/ibet-Prime/cmd USER apl RUN mkdir -p /app/ibet-Prime/cov/ diff --git a/tests/account_config.py b/tests/account_config.py index 67f6d60b..12e84fcb 100644 --- a/tests/account_config.py +++ b/tests/account_config.py @@ -21,12 +21,12 @@ import yaml from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) # Account Address(from local config) diff --git a/tests/model/blockchain/test_E2EMessaging.py b/tests/app/model/blockchain/test_E2EMessaging.py similarity index 99% rename from tests/model/blockchain/test_E2EMessaging.py rename to tests/app/model/blockchain/test_E2EMessaging.py index 9ad1b1fc..7ef20aaa 100644 --- a/tests/model/blockchain/test_E2EMessaging.py +++ b/tests/app/model/blockchain/test_E2EMessaging.py @@ -30,7 +30,7 @@ from eth_keyfile import decode_keyfile_json from web3 import Web3 from web3.exceptions import TimeExhausted -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.exceptions import SendTransactionError from app.model.blockchain import E2EMessaging @@ -39,7 +39,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) class TestSendMessage: diff --git a/tests/model/blockchain/test_FreezeLog.py b/tests/app/model/blockchain/test_FreezeLog.py similarity index 98% rename from tests/model/blockchain/test_FreezeLog.py rename to tests/app/model/blockchain/test_FreezeLog.py index 5dcbf8a6..9fddb383 100644 --- a/tests/model/blockchain/test_FreezeLog.py +++ b/tests/app/model/blockchain/test_FreezeLog.py @@ -23,7 +23,7 @@ import pytest from web3 import Web3 from web3.exceptions import TimeExhausted -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.exceptions import SendTransactionError from app.model.blockchain import FreezeLogContract @@ -34,7 +34,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) class TestRecordLog: diff --git a/tests/model/blockchain/test_PersonalInfo.py b/tests/app/model/blockchain/test_PersonalInfo.py similarity index 98% rename from tests/model/blockchain/test_PersonalInfo.py rename to tests/app/model/blockchain/test_PersonalInfo.py index 1ad69893..a84c98af 100644 --- a/tests/model/blockchain/test_PersonalInfo.py +++ b/tests/app/model/blockchain/test_PersonalInfo.py @@ -28,7 +28,7 @@ from eth_keyfile import decode_keyfile_json from web3 import Web3 from web3.exceptions import ContractLogicError, TimeExhausted -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.exceptions import ContractRevertError, SendTransactionError from app.model.blockchain import PersonalInfoContract @@ -39,7 +39,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) def initialize(issuer, db): @@ -161,8 +161,8 @@ async def test_normal_2(self, db): "address": "test", "email": "test", "birth": "test", - "is_corporate": "test", - "tax_category": "test", + "is_corporate": None, + "tax_category": None, } ########################################################################### @@ -230,8 +230,8 @@ async def test_error_1(self, db): "address": "test", "email": "test", "birth": "test", - "is_corporate": "test", - "tax_category": "test", + "is_corporate": None, + "tax_category": None, } # @@ -274,8 +274,8 @@ async def test_error_2(self, db): "address": "test", "email": "test", "birth": "test", - "is_corporate": "test", - "tax_category": "test", + "is_corporate": None, + "tax_category": None, } diff --git a/tests/model/blockchain/test_TokenList.py b/tests/app/model/blockchain/test_TokenList.py similarity index 98% rename from tests/model/blockchain/test_TokenList.py rename to tests/app/model/blockchain/test_TokenList.py index b29fb88c..88687b28 100755 --- a/tests/model/blockchain/test_TokenList.py +++ b/tests/app/model/blockchain/test_TokenList.py @@ -25,7 +25,7 @@ from eth_keyfile import decode_keyfile_json from web3 import Web3 from web3.exceptions import ContractLogicError, InvalidAddress, ValidationError -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.exceptions import ContractRevertError, SendTransactionError @@ -37,7 +37,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) @pytest.fixture diff --git a/tests/model/blockchain/test_exchange_IbetExchangeInterface.py b/tests/app/model/blockchain/test_exchange_IbetExchangeInterface.py similarity index 98% rename from tests/model/blockchain/test_exchange_IbetExchangeInterface.py rename to tests/app/model/blockchain/test_exchange_IbetExchangeInterface.py index a556db06..a66ac9ad 100644 --- a/tests/model/blockchain/test_exchange_IbetExchangeInterface.py +++ b/tests/app/model/blockchain/test_exchange_IbetExchangeInterface.py @@ -20,7 +20,7 @@ import pytest from eth_keyfile import decode_keyfile_json from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.model.blockchain import IbetExchangeInterface, IbetStraightBondContract from app.utils.contract_utils import ContractUtils @@ -28,7 +28,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) def deploy_escrow_contract(): diff --git a/tests/model/blockchain/test_exchange_IbetSecurityTokenEscrow.py b/tests/app/model/blockchain/test_exchange_IbetSecurityTokenEscrow.py similarity index 99% rename from tests/model/blockchain/test_exchange_IbetSecurityTokenEscrow.py rename to tests/app/model/blockchain/test_exchange_IbetSecurityTokenEscrow.py index 9fb05574..4e5a6a72 100644 --- a/tests/model/blockchain/test_exchange_IbetSecurityTokenEscrow.py +++ b/tests/app/model/blockchain/test_exchange_IbetSecurityTokenEscrow.py @@ -24,7 +24,7 @@ from eth_keyfile import decode_keyfile_json from web3 import Web3 from web3.exceptions import ContractLogicError, TimeExhausted -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.exceptions import ContractRevertError, SendTransactionError from app.model.blockchain import IbetSecurityTokenEscrow, IbetStraightBondContract @@ -36,7 +36,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) def deploy_security_token_escrow_contract(): diff --git a/tests/model/blockchain/test_token_IbetShare.py b/tests/app/model/blockchain/test_token_IbetShare.py similarity index 98% rename from tests/model/blockchain/test_token_IbetShare.py rename to tests/app/model/blockchain/test_token_IbetShare.py index 173d79de..66261937 100644 --- a/tests/model/blockchain/test_token_IbetShare.py +++ b/tests/app/model/blockchain/test_token_IbetShare.py @@ -19,7 +19,7 @@ import time from binascii import Error -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from unittest import mock from unittest.mock import ANY, AsyncMock, patch @@ -52,7 +52,7 @@ from app.utils.contract_utils import ContractUtils from config import TOKEN_CACHE_TTL, ZERO_ADDRESS from tests.account_config import config_eth_account -from tests.utils.contract_utils import ( +from tests.contract_utils import ( IbetSecurityTokenContractTestUtils, PersonalInfoContractTestUtils, ) @@ -317,6 +317,7 @@ async def test_normal_1(self, db): assert share_contract.tradable_exchange_contract_address == ZERO_ADDRESS assert share_contract.status is True assert share_contract.personal_info_contract_address == ZERO_ADDRESS + assert share_contract.require_personal_info_registered is True assert share_contract.transferable is False assert share_contract.is_offering is False assert share_contract.transfer_approval_required is False @@ -371,6 +372,7 @@ async def test_normal_2(self, db): "tradable_exchange_contract_address": "0x1234567890123456789012345678901234567890", "status": False, "personal_info_contract_address": "0x1234567890123456789012345678901234567891", + "require_personal_info_registered": True, "transferable": True, "is_offering": True, "transfer_approval_required": True, @@ -386,10 +388,10 @@ async def test_normal_2(self, db): token_cache = TokenCache() token_cache.token_address = contract_address token_cache.attributes = token_attr - token_cache.cached_datetime = datetime.utcnow() - token_cache.expiration_datetime = datetime.utcnow() + timedelta( - seconds=TOKEN_CACHE_TTL - ) + token_cache.cached_datetime = datetime.now(UTC).replace(tzinfo=None) + token_cache.expiration_datetime = datetime.now(UTC).replace( + tzinfo=None + ) + timedelta(seconds=TOKEN_CACHE_TTL) db.add(token_cache) db.commit() @@ -413,6 +415,7 @@ async def test_normal_2(self, db): share_contract.personal_info_contract_address == "0x1234567890123456789012345678901234567891" ) + assert share_contract.require_personal_info_registered is True assert share_contract.transferable is True assert share_contract.is_offering is True assert share_contract.transfer_approval_required is True @@ -467,6 +470,7 @@ async def test_normal_3(self, db): "tradable_exchange_contract_address": "0x1234567890123456789012345678901234567890", "status": False, "personal_info_contract_address": "0x1234567890123456789012345678901234567891", + "require_personal_info_registered": False, "transferable": True, "is_offering": True, "transfer_approval_required": True, @@ -482,17 +486,17 @@ async def test_normal_3(self, db): token_cache = TokenCache() token_cache.token_address = contract_address token_cache.attributes = token_attr - token_cache.cached_datetime = datetime.utcnow() - token_cache.expiration_datetime = datetime.utcnow() + timedelta( - seconds=TOKEN_CACHE_TTL - ) + token_cache.cached_datetime = datetime.now(UTC).replace(tzinfo=None) + token_cache.expiration_datetime = datetime.now(UTC).replace( + tzinfo=None + ) + timedelta(seconds=TOKEN_CACHE_TTL) db.add(token_cache) db.commit() # updated token attribute _token_attr_update = TokenAttrUpdate() _token_attr_update.token_address = contract_address - _token_attr_update.updated_datetime = datetime.utcnow() + _token_attr_update.updated_datetime = datetime.now(UTC).replace(tzinfo=None) db.add(_token_attr_update) db.commit() @@ -510,6 +514,7 @@ async def test_normal_3(self, db): assert share_contract.tradable_exchange_contract_address == ZERO_ADDRESS assert share_contract.status is True assert share_contract.personal_info_contract_address == ZERO_ADDRESS + assert share_contract.require_personal_info_registered is True assert share_contract.transferable is False assert share_contract.is_offering is False assert share_contract.transfer_approval_required is False @@ -541,6 +546,7 @@ async def test_normal_4(self, db): assert share_contract.tradable_exchange_contract_address == ZERO_ADDRESS assert share_contract.status is True assert share_contract.personal_info_contract_address == ZERO_ADDRESS + assert share_contract.require_personal_info_registered is True assert share_contract.transferable is False assert share_contract.is_offering is False assert share_contract.transfer_approval_required is False @@ -594,7 +600,7 @@ async def test_normal_1(self, db): # update _data = {} _add_data = UpdateParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) await share_contract.update( data=_add_data, tx_from=issuer_address, private_key=private_key ) @@ -607,6 +613,7 @@ async def test_normal_1(self, db): assert share_contract.dividends == 0.0000000000001 assert share_contract.tradable_exchange_contract_address == ZERO_ADDRESS assert share_contract.personal_info_contract_address == ZERO_ADDRESS + assert share_contract.require_personal_info_registered is True assert share_contract.transferable is False assert share_contract.status is True assert share_contract.is_offering is False @@ -658,6 +665,7 @@ async def test_normal_2(self, db): "dividends": 0.01, "tradable_exchange_contract_address": "0x0000000000000000000000000000000000000001", "personal_info_contract_address": "0x0000000000000000000000000000000000000002", + "require_personal_info_registered": False, "transferable": False, "status": False, "is_offering": True, @@ -669,7 +677,7 @@ async def test_normal_2(self, db): "memo": "memo_test", } _add_data = UpdateParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) await share_contract.update( data=_add_data, tx_from=issuer_address, private_key=private_key ) @@ -688,6 +696,7 @@ async def test_normal_2(self, db): share_contract.personal_info_contract_address == "0x0000000000000000000000000000000000000002" ) + assert share_contract.require_personal_info_registered is False assert share_contract.transferable is False assert share_contract.status is False assert share_contract.is_offering is True @@ -901,7 +910,7 @@ async def test_error_5(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # update @@ -1259,7 +1268,7 @@ async def test_error_6(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # transfer @@ -1725,7 +1734,7 @@ async def test_error_5_3(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # bulk transfer @@ -1776,7 +1785,7 @@ async def test_normal_1(self, db): # additional issue _data = {"account_address": issuer_address, "amount": 10} _add_data = AdditionalIssueParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) await share_contract.additional_issue( data=_add_data, tx_from=issuer_address, private_key=private_key ) @@ -2004,7 +2013,7 @@ async def test_error_6(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # additional issue @@ -2054,7 +2063,7 @@ async def test_error_7(self, db): # additional issue _data = {"account_address": issuer_address, "amount": 10} _add_data = AdditionalIssueParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) # mock # NOTE: Ganacheがrevertする際にweb3.pyからraiseされるExceptionはGethと異なる @@ -2109,7 +2118,7 @@ async def test_normal_1(self, db): # redeem _data = {"account_address": issuer_address, "amount": 10} _add_data = RedeemParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) await share_contract.redeem( data=_add_data, tx_from=issuer_address, private_key=private_key ) @@ -2337,7 +2346,7 @@ async def test_error_6(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # redeem @@ -2381,7 +2390,7 @@ async def test_error_7(self, db): # redeem _data = {"account_address": issuer_address, "amount": 100_000_000} _add_data = RedeemParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) # mock # NOTE: Ganacheがrevertする際にweb3.pyからraiseされるExceptionはGethと異なる @@ -2502,7 +2511,7 @@ class TestCheckAttrUpdate: # not exists @pytest.mark.asyncio async def test_normal_1(self, async_db): - before_datetime = datetime.utcnow() + before_datetime = datetime.now(UTC).replace(tzinfo=None) # Test share_contract = IbetShareContract(self.token_address) @@ -2515,9 +2524,9 @@ async def test_normal_1(self, async_db): # prev data exists @pytest.mark.asyncio async def test_normal_2(self, async_db): - before_datetime = datetime.utcnow() + before_datetime = datetime.now(UTC).replace(tzinfo=None) time.sleep(1) - after_datetime = datetime.utcnow() + after_datetime = datetime.now(UTC).replace(tzinfo=None) # prepare data _update = TokenAttrUpdate() @@ -2537,9 +2546,9 @@ async def test_normal_2(self, async_db): # next data exists @pytest.mark.asyncio async def test_normal_3(self, async_db): - before_datetime = datetime.utcnow() + before_datetime = datetime.now(UTC).replace(tzinfo=None) time.sleep(1) - after_datetime = datetime.utcnow() + after_datetime = datetime.now(UTC).replace(tzinfo=None) # prepare data _update = TokenAttrUpdate() @@ -2589,7 +2598,7 @@ async def test_normal_2(self, async_db, freezer): # prepare data _update = TokenAttrUpdate() _update.token_address = self.token_address - _update.updated_datetime = datetime.utcnow() + _update.updated_datetime = datetime.now(UTC).replace(tzinfo=None) async_db.add(_update) await async_db.commit() @@ -3620,7 +3629,7 @@ async def test_error_4(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # lock @@ -4086,7 +4095,7 @@ async def test_error_4(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # forceUnlock diff --git a/tests/model/blockchain/test_token_IbetStraightBond.py b/tests/app/model/blockchain/test_token_IbetStraightBond.py similarity index 98% rename from tests/model/blockchain/test_token_IbetStraightBond.py rename to tests/app/model/blockchain/test_token_IbetStraightBond.py index ca802de4..cd2006ee 100644 --- a/tests/model/blockchain/test_token_IbetStraightBond.py +++ b/tests/app/model/blockchain/test_token_IbetStraightBond.py @@ -19,7 +19,7 @@ import time from binascii import Error -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from unittest import mock from unittest.mock import ANY, MagicMock, patch @@ -52,7 +52,7 @@ from app.utils.contract_utils import ContractUtils from config import DEFAULT_CURRENCY, TOKEN_CACHE_TTL, ZERO_ADDRESS from tests.account_config import config_eth_account -from tests.utils.contract_utils import ( +from tests.contract_utils import ( IbetSecurityTokenContractTestUtils, PersonalInfoContractTestUtils, ) @@ -309,7 +309,7 @@ async def test_normal_1(self, db): ) # get token data - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) bond_contract = await IbetStraightBondContract(contract_address).get() # assertion @@ -323,6 +323,7 @@ async def test_normal_1(self, db): assert bond_contract.tradable_exchange_contract_address == ZERO_ADDRESS assert bond_contract.status is True assert bond_contract.personal_info_contract_address == ZERO_ADDRESS + assert bond_contract.require_personal_info_registered is True assert bond_contract.transferable is False assert bond_contract.is_offering is False assert bond_contract.transfer_approval_required is False @@ -396,6 +397,7 @@ async def test_normal_2(self, db): "tradable_exchange_contract_address": "0x1234567890123456789012345678901234567890", "status": False, "personal_info_contract_address": "0x1234567890123456789012345678901234567891", + "require_personal_info_registered": True, "transferable": True, "is_offering": True, "transfer_approval_required": True, @@ -430,10 +432,10 @@ async def test_normal_2(self, db): token_cache = TokenCache() token_cache.token_address = contract_address token_cache.attributes = token_attr - token_cache.cached_datetime = datetime.utcnow() - token_cache.expiration_datetime = datetime.utcnow() + timedelta( - seconds=TOKEN_CACHE_TTL - ) + token_cache.cached_datetime = datetime.now(UTC).replace(tzinfo=None) + token_cache.expiration_datetime = datetime.now(UTC).replace( + tzinfo=None + ) + timedelta(seconds=TOKEN_CACHE_TTL) db.add(token_cache) db.commit() @@ -457,6 +459,7 @@ async def test_normal_2(self, db): bond_contract.personal_info_contract_address == "0x1234567890123456789012345678901234567891" ) + assert bond_contract.require_personal_info_registered is True assert bond_contract.transferable is True assert bond_contract.is_offering is True assert bond_contract.transfer_approval_required is True @@ -530,6 +533,7 @@ async def test_normal_3(self, db): "tradable_exchange_contract_address": "0x1234567890123456789012345678901234567890", "status": False, "personal_info_contract_address": "0x1234567890123456789012345678901234567891", + "require_personal_info_registered": False, "transferable": True, "is_offering": True, "transfer_approval_required": True, @@ -564,16 +568,16 @@ async def test_normal_3(self, db): token_cache = TokenCache() token_cache.token_address = contract_address token_cache.attributes = token_attr - token_cache.cached_datetime = datetime.utcnow() - token_cache.expiration_datetime = datetime.utcnow() + timedelta( - seconds=TOKEN_CACHE_TTL - ) + token_cache.cached_datetime = datetime.now(UTC).replace(tzinfo=None) + token_cache.expiration_datetime = datetime.now(UTC).replace( + tzinfo=None + ) + timedelta(seconds=TOKEN_CACHE_TTL) db.add(token_cache) # updated token attribute _token_attr_update = TokenAttrUpdate() _token_attr_update.token_address = contract_address - _token_attr_update.updated_datetime = datetime.utcnow() + _token_attr_update.updated_datetime = datetime.now(UTC).replace(tzinfo=None) db.add(_token_attr_update) db.commit() @@ -591,6 +595,7 @@ async def test_normal_3(self, db): assert bond_contract.tradable_exchange_contract_address == ZERO_ADDRESS assert bond_contract.status is True assert bond_contract.personal_info_contract_address == ZERO_ADDRESS + assert bond_contract.require_personal_info_registered is True assert bond_contract.transferable is False assert bond_contract.is_offering is False assert bond_contract.transfer_approval_required is False @@ -640,6 +645,7 @@ async def test_normal_4(self, db): assert bond_contract.tradable_exchange_contract_address == ZERO_ADDRESS assert bond_contract.status is True assert bond_contract.personal_info_contract_address == ZERO_ADDRESS + assert bond_contract.require_personal_info_registered is True assert bond_contract.transferable is False assert bond_contract.is_offering is False assert bond_contract.transfer_approval_required is False @@ -714,7 +720,7 @@ async def test_normal_1(self, db): # update _data = {} _add_data = UpdateParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) await bond_contract.update( data=_add_data, tx_from=issuer_address, private_key=private_key ) @@ -752,6 +758,7 @@ async def test_normal_1(self, db): assert bond_contract.is_redeemed is False assert bond_contract.tradable_exchange_contract_address == ZERO_ADDRESS assert bond_contract.personal_info_contract_address == ZERO_ADDRESS + assert bond_contract.require_personal_info_registered is True assert bond_contract.contact_information == "" assert bond_contract.privacy_policy == "" assert bond_contract.transfer_approval_required is False @@ -808,13 +815,14 @@ async def test_normal_2(self, db): "is_redeemed": True, "tradable_exchange_contract_address": "0x0000000000000000000000000000000000000001", "personal_info_contract_address": "0x0000000000000000000000000000000000000002", + "require_personal_info_registered": False, "contact_information": "contact info test", "privacy_policy": "privacy policy test", "transfer_approval_required": True, "memo": "memo test", } _add_data = UpdateParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) await bond_contract.update( data=_add_data, tx_from=issuer_address, private_key=private_key ) @@ -854,6 +862,7 @@ async def test_normal_2(self, db): bond_contract.personal_info_contract_address == "0x0000000000000000000000000000000000000002" ) + assert bond_contract.require_personal_info_registered is False assert bond_contract.contact_information == "contact info test" assert bond_contract.privacy_policy == "privacy policy test" assert bond_contract.transfer_approval_required is True @@ -1116,7 +1125,7 @@ async def test_error_5(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # update @@ -1488,7 +1497,7 @@ async def test_error_6(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # transfer @@ -1968,7 +1977,7 @@ async def test_error_5_3(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # bulk transfer @@ -2021,7 +2030,7 @@ async def test_normal_1(self, db): # additional issue _data = {"account_address": issuer_address, "amount": 10} _add_data = AdditionalIssueParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) await bond_contract.additional_issue( data=_add_data, tx_from=issuer_address, private_key=private_key ) @@ -2257,7 +2266,7 @@ async def test_error_6(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # additional issue @@ -2309,7 +2318,7 @@ async def test_error_7(self, db): # additional issue _data = {"account_address": issuer_address, "amount": 10} _add_data = AdditionalIssueParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) # mock # NOTE: Ganacheがrevertする際にweb3.pyからraiseされるExceptionはGethと異なる @@ -2366,7 +2375,7 @@ async def test_normal_1(self, db): # redeem _data = {"account_address": issuer_address, "amount": 10} _add_data = RedeemParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) await bond_contract.redeem( data=_add_data, tx_from=issuer_address, private_key=private_key ) @@ -2602,7 +2611,7 @@ async def test_error_6(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # redeem @@ -2648,7 +2657,7 @@ async def test_error_7(self, db): # redeem _data = {"account_address": issuer_address, "amount": 100_000_000} _add_data = RedeemParams(**_data) - pre_datetime = datetime.utcnow() + pre_datetime = datetime.now(UTC).replace(tzinfo=None) # mock # NOTE: Ganacheがrevertする際にweb3.pyからraiseされるExceptionはGethと異なる @@ -2773,7 +2782,7 @@ class TestCheckAttrUpdate: # not exists @pytest.mark.asyncio async def test_normal_1(self, async_db): - before_datetime = datetime.utcnow() + before_datetime = datetime.now(UTC).replace(tzinfo=None) # Test bond_contract = IbetStraightBondContract(self.token_address) @@ -2786,9 +2795,9 @@ async def test_normal_1(self, async_db): # prev data exists @pytest.mark.asyncio async def test_normal_2(self, async_db): - before_datetime = datetime.utcnow() + before_datetime = datetime.now(UTC).replace(tzinfo=None) time.sleep(1) - after_datetime = datetime.utcnow() + after_datetime = datetime.now(UTC).replace(tzinfo=None) # prepare data _update = TokenAttrUpdate() @@ -2808,9 +2817,9 @@ async def test_normal_2(self, async_db): # next data exists @pytest.mark.asyncio async def test_normal_3(self, async_db): - before_datetime = datetime.utcnow() + before_datetime = datetime.now(UTC).replace(tzinfo=None) time.sleep(1) - after_datetime = datetime.utcnow() + after_datetime = datetime.now(UTC).replace(tzinfo=None) # prepare data _update = TokenAttrUpdate() @@ -2860,7 +2869,7 @@ async def test_normal_2(self, async_db, freezer): # prepare data _update = TokenAttrUpdate() _update.token_address = self.token_address - _update.updated_datetime = datetime.utcnow() + _update.updated_datetime = datetime.now(UTC).replace(tzinfo=None) async_db.add(_update) await async_db.commit() @@ -3915,7 +3924,7 @@ async def test_error_4(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # lock @@ -4393,7 +4402,7 @@ async def test_error_4(self, db): # mock Web3_send_raw_transaction = patch( target="web3.eth.async_eth.AsyncEth.wait_for_transaction_receipt", - side_effect=TransactionNotFound, + side_effect=TransactionNotFound(message=""), ) # forceUnlock diff --git a/tests/test_app_routers_accounts_GET.py b/tests/app/test_accounts_GET.py similarity index 100% rename from tests/test_app_routers_accounts_GET.py rename to tests/app/test_accounts_GET.py diff --git a/tests/test_app_routers_accounts_POST.py b/tests/app/test_accounts_POST.py similarity index 100% rename from tests/test_app_routers_accounts_POST.py rename to tests/app/test_accounts_POST.py diff --git a/tests/test_app_routers_accounts_{issuer_address}_DELETE.py b/tests/app/test_accounts_{issuer_address}_DELETE.py similarity index 100% rename from tests/test_app_routers_accounts_{issuer_address}_DELETE.py rename to tests/app/test_accounts_{issuer_address}_DELETE.py diff --git a/tests/test_app_routers_accounts_{issuer_address}_GET.py b/tests/app/test_accounts_{issuer_address}_GET.py similarity index 100% rename from tests/test_app_routers_accounts_{issuer_address}_GET.py rename to tests/app/test_accounts_{issuer_address}_GET.py diff --git a/tests/test_app_routers_accounts_{issuer_address}_authtoken_DELETE.py b/tests/app/test_accounts_{issuer_address}_authtoken_DELETE.py similarity index 100% rename from tests/test_app_routers_accounts_{issuer_address}_authtoken_DELETE.py rename to tests/app/test_accounts_{issuer_address}_authtoken_DELETE.py diff --git a/tests/test_app_routers_accounts_{issuer_address}_authtoken_POST.py b/tests/app/test_accounts_{issuer_address}_authtoken_POST.py similarity index 100% rename from tests/test_app_routers_accounts_{issuer_address}_authtoken_POST.py rename to tests/app/test_accounts_{issuer_address}_authtoken_POST.py diff --git a/tests/test_app_routers_accounts_{issuer_address}_eoa_password_POST.py b/tests/app/test_accounts_{issuer_address}_eoa_password_POST.py similarity index 100% rename from tests/test_app_routers_accounts_{issuer_address}_eoa_password_POST.py rename to tests/app/test_accounts_{issuer_address}_eoa_password_POST.py diff --git a/tests/test_app_routers_accounts_{issuer_address}_rsa_passphrase_POST.py b/tests/app/test_accounts_{issuer_address}_rsa_passphrase_POST.py similarity index 100% rename from tests/test_app_routers_accounts_{issuer_address}_rsa_passphrase_POST.py rename to tests/app/test_accounts_{issuer_address}_rsa_passphrase_POST.py diff --git a/tests/test_app_routers_accounts_{issuer_address}_rsakey_POST.py b/tests/app/test_accounts_{issuer_address}_rsakey_POST.py similarity index 100% rename from tests/test_app_routers_accounts_{issuer_address}_rsakey_POST.py rename to tests/app/test_accounts_{issuer_address}_rsakey_POST.py diff --git a/tests/test_app_routers_block_number_GET.py b/tests/app/test_block_number_GET.py similarity index 100% rename from tests/test_app_routers_block_number_GET.py rename to tests/app/test_block_number_GET.py diff --git a/tests/test_app_routers_blockchain_explorer_block_data_GET.py b/tests/app/test_blockchain_explorer_block_data_GET.py similarity index 100% rename from tests/test_app_routers_blockchain_explorer_block_data_GET.py rename to tests/app/test_blockchain_explorer_block_data_GET.py diff --git a/tests/test_app_routers_blockchain_explorer_block_data_{block_number}_GET.py b/tests/app/test_blockchain_explorer_block_data_{block_number}_GET.py similarity index 100% rename from tests/test_app_routers_blockchain_explorer_block_data_{block_number}_GET.py rename to tests/app/test_blockchain_explorer_block_data_{block_number}_GET.py diff --git a/tests/test_app_routers_blockchain_explorer_tx_data_GET.py b/tests/app/test_blockchain_explorer_tx_data_GET.py similarity index 100% rename from tests/test_app_routers_blockchain_explorer_tx_data_GET.py rename to tests/app/test_blockchain_explorer_tx_data_GET.py diff --git a/tests/test_app_routers_blockchain_explorer_tx_data_{hash}_GET.py b/tests/app/test_blockchain_explorer_tx_data_{hash}_GET.py similarity index 99% rename from tests/test_app_routers_blockchain_explorer_tx_data_{hash}_GET.py rename to tests/app/test_blockchain_explorer_tx_data_{hash}_GET.py index f8d82289..137807fb 100644 --- a/tests/test_app_routers_blockchain_explorer_tx_data_{hash}_GET.py +++ b/tests/app/test_blockchain_explorer_tx_data_{hash}_GET.py @@ -70,7 +70,7 @@ def insert_token_data(session, token_info): token.issuer_address = "" token.token_address = token_info.get("token_address") token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 session.add(token) session.commit() diff --git a/tests/test_app_routers_bond_bulk_transfer_GET.py b/tests/app/test_bond_bulk_transfer_GET.py similarity index 97% rename from tests/test_app_routers_bond_bulk_transfer_GET.py rename to tests/app/test_bond_bulk_transfer_GET.py index 0292760a..d4617f59 100644 --- a/tests/test_app_routers_bond_bulk_transfer_GET.py +++ b/tests/app/test_bond_bulk_transfer_GET.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime +from datetime import UTC, datetime import pytest import pytz @@ -70,7 +70,7 @@ def test_normal_1(self, client, db): db.add(account) # prepare data : BulkTransferUpload - utc_now = datetime.utcnow() + utc_now = datetime.now(UTC).replace(tzinfo=None) for i in range(0, 3): bulk_transfer_upload = BulkTransferUpload() bulk_transfer_upload.issuer_address = self.upload_issuer_list[i]["address"] @@ -110,7 +110,7 @@ def test_normal_1(self, client, db): @pytest.mark.freeze_time("2021-05-20 12:34:56") def test_normal_2(self, client, db): # prepare data : BulkTransferUpload - utc_now = datetime.utcnow() + utc_now = datetime.now(UTC).replace(tzinfo=None) for i in range(0, 3): bulk_transfer_upload = BulkTransferUpload() bulk_transfer_upload.issuer_address = self.upload_issuer_list[i]["address"] diff --git a/tests/test_app_routers_bond_bulk_transfer_POST.py b/tests/app/test_bond_bulk_transfer_POST.py similarity index 98% rename from tests/test_app_routers_bond_bulk_transfer_POST.py rename to tests/app/test_bond_bulk_transfer_POST.py index 19f3568d..6e9059f0 100644 --- a/tests/test_app_routers_bond_bulk_transfer_POST.py +++ b/tests/app/test_bond_bulk_transfer_POST.py @@ -83,7 +83,7 @@ def test_normal_1(self, client, db): _token.issuer_address = self.admin_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -172,7 +172,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.admin_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -254,7 +254,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.from_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -693,7 +693,7 @@ def test_error_10(self, client, db): _token.token_address = self.req_tokens[0] _token.abi = "" _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -743,7 +743,7 @@ def test_error_11_1(self, client, db): _token.issuer_address = self.from_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -801,7 +801,7 @@ def test_error_11_2(self, client, db): _token.issuer_address = self.from_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -859,7 +859,7 @@ def test_error_11_3(self, client, db): _token.issuer_address = self.admin_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_bond_bulk_transfer_{upload_id}_GET.py b/tests/app/test_bond_bulk_transfer_{upload_id}_GET.py similarity index 100% rename from tests/test_app_routers_bond_bulk_transfer_{upload_id}_GET.py rename to tests/app/test_bond_bulk_transfer_{upload_id}_GET.py diff --git a/tests/test_app_routers_bond_lock_events_GET.py b/tests/app/test_bond_lock_events_GET.py similarity index 97% rename from tests/test_app_routers_bond_lock_events_GET.py rename to tests/app/test_bond_lock_events_GET.py index f4425160..4b50e4e4 100644 --- a/tests/test_app_routers_bond_lock_events_GET.py +++ b/tests/app/test_bond_lock_events_GET.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime +from datetime import UTC, datetime from unittest import mock from unittest.mock import ANY, AsyncMock @@ -56,7 +56,7 @@ def setup_data(self, db: Session, token_status: int = 1): _token.tx_hash = "" _token.abi = "" _token.token_status = token_status - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token @@ -67,7 +67,7 @@ def setup_data(self, db: Session, token_status: int = 1): _token.tx_hash = "" _token.abi = "" _token.token_status = token_status - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -80,7 +80,7 @@ def setup_data(self, db: Session, token_status: int = 1): _lock.account_address = self.account_address_1 _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -92,7 +92,7 @@ def setup_data(self, db: Session, token_status: int = 1): _lock.account_address = self.account_address_2 _lock.value = 1 _lock.data = {"message": "locked_2"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -105,7 +105,7 @@ def setup_data(self, db: Session, token_status: int = 1): _unlock.recipient_address = self.other_account_address_1 _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) _unlock = IDXUnlock() @@ -118,7 +118,7 @@ def setup_data(self, db: Session, token_status: int = 1): _unlock.recipient_address = self.other_account_address_2 _unlock.value = 1 _unlock.data = {"message": "unlocked_2"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -207,7 +207,7 @@ def test_normal_1(self, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_GET.py b/tests/app/test_bond_tokens_GET.py similarity index 95% rename from tests/test_app_routers_bond_tokens_GET.py rename to tests/app/test_bond_tokens_GET.py index 835c13d5..b3f400c2 100644 --- a/tests/test_app_routers_bond_tokens_GET.py +++ b/tests/app/test_bond_tokens_GET.py @@ -58,7 +58,7 @@ def test_normal_2(self, mock_get, client, db): token.issuer_address = issuer_address_1 token.token_address = "token_address_test1" token.abi = "abi_test1" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() _issue_datetime = ( @@ -96,6 +96,7 @@ def test_normal_2(self, mock_get, client, db): mock_token.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token.require_personal_info_registered = True mock_token.interest_payment_date = [ "interestPaymentDate1_test1", "interestPaymentDate2_test1", @@ -156,13 +157,14 @@ def test_normal_2(self, mock_get, client, db): "is_offering": False, "tradable_exchange_contract_address": "0x1234567890abCdFe1234567890ABCdFE12345678", "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "contact_information": "contactInformation_test1", "privacy_policy": "privacyPolicy_test1", "issue_datetime": _issue_datetime, "token_status": 1, "transfer_approval_required": True, "memo": "memo_test1", - "contract_version": TokenVersion.V_23_12, + "contract_version": TokenVersion.V_24_06, } ] @@ -185,7 +187,7 @@ def test_normal_3(self, mock_get, client, db): token_1.issuer_address = issuer_address_1 token_1.token_address = "token_address_test1" token_1.abi = "abi_test1" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() _issue_datetime_1 = ( @@ -222,6 +224,7 @@ def test_normal_3(self, mock_get, client, db): mock_token_1.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token_1.require_personal_info_registered = True mock_token_1.interest_payment_date = [ "interestPaymentDate1_test1", "interestPaymentDate2_test1", @@ -249,7 +252,7 @@ def test_normal_3(self, mock_get, client, db): token_2.token_address = "token_address_test2" token_2.abi = "abi_test2" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() _issue_datetime_2 = ( @@ -286,6 +289,7 @@ def test_normal_3(self, mock_get, client, db): mock_token_2.personal_info_contract_address = ( "0x1234567890abcdFE1234567890ABcdfE12345681" ) + mock_token_2.require_personal_info_registered = False mock_token_2.interest_payment_date = [ "interestPaymentDate1_test2", "interestPaymentDate2_test2", @@ -350,13 +354,14 @@ def test_normal_3(self, mock_get, client, db): "is_offering": False, "tradable_exchange_contract_address": "0x1234567890abCdFe1234567890ABCdFE12345678", "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "contact_information": "contactInformation_test1", "privacy_policy": "privacyPolicy_test1", "issue_datetime": _issue_datetime_1, "token_status": 1, "transfer_approval_required": True, "memo": "memo_test1", - "contract_version": TokenVersion.V_23_12, + "contract_version": TokenVersion.V_24_06, }, { "issuer_address": token_2.issuer_address, @@ -395,13 +400,14 @@ def test_normal_3(self, mock_get, client, db): "is_offering": False, "tradable_exchange_contract_address": "0x1234567890AbcdfE1234567890abcdfE12345680", "personal_info_contract_address": "0x1234567890abcdFE1234567890ABcdfE12345681", + "require_personal_info_registered": False, "contact_information": "contactInformation_test2", "privacy_policy": "privacyPolicy_test2", "issue_datetime": _issue_datetime_2, "token_status": 0, "transfer_approval_required": False, "memo": "memo_test2", - "contract_version": TokenVersion.V_23_12, + "contract_version": TokenVersion.V_24_06, }, ] @@ -423,7 +429,7 @@ def test_normal_4(self, client, db): token.issuer_address = issuer_address_1 token.token_address = "token_address_test1" token.abi = "abi_test1" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) resp = client.get(self.apiurl, headers={"issuer-address": issuer_address_2}) @@ -446,7 +452,7 @@ def test_normal_5(self, mock_get, client, db): token_1.issuer_address = issuer_address_1 token_1.token_address = "token_address_test1" token_1.abi = "abi_test1" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() _issue_datetime = ( @@ -484,6 +490,7 @@ def test_normal_5(self, mock_get, client, db): mock_token.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token.require_personal_info_registered = True mock_token.interest_payment_date = [ "interestPaymentDate1_test1", "interestPaymentDate2_test1", @@ -511,7 +518,7 @@ def test_normal_5(self, mock_get, client, db): token_2.issuer_address = issuer_address_2 token_2.token_address = "token_address_test1" token_2.abi = "abi_test1" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) resp = client.get(self.apiurl, headers={"issuer-address": issuer_address_1}) @@ -554,13 +561,14 @@ def test_normal_5(self, mock_get, client, db): "is_offering": False, "tradable_exchange_contract_address": "0x1234567890abCdFe1234567890ABCdFE12345678", "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "contact_information": "contactInformation_test1", "privacy_policy": "privacyPolicy_test1", "issue_datetime": _issue_datetime, "token_status": 1, "transfer_approval_required": True, "memo": "memo_test1", - "contract_version": TokenVersion.V_23_12, + "contract_version": TokenVersion.V_24_06, } ] @@ -583,7 +591,7 @@ def test_normal_6(self, mock_get, client, db): token_1.issuer_address = issuer_address_1 token_1.token_address = "token_address_test1" token_1.abi = "abi_test1" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() _issue_datetime_1 = ( @@ -620,6 +628,7 @@ def test_normal_6(self, mock_get, client, db): mock_token_1.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token_1.require_personal_info_registered = True mock_token_1.interest_payment_date = [ "interestPaymentDate1_test1", "interestPaymentDate2_test1", @@ -647,7 +656,7 @@ def test_normal_6(self, mock_get, client, db): token_2.token_address = "token_address_test2" token_2.abi = "abi_test2" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() _issue_datetime_2 = ( @@ -684,6 +693,7 @@ def test_normal_6(self, mock_get, client, db): mock_token_2.personal_info_contract_address = ( "0x1234567890abcdFE1234567890ABcdfE12345681" ) + mock_token_2.require_personal_info_registered = True mock_token_2.interest_payment_date = [ "interestPaymentDate1_test2", "interestPaymentDate2_test2", @@ -715,7 +725,7 @@ def test_normal_6(self, mock_get, client, db): token_3.issuer_address = issuer_address_2 token_3.token_address = "token_address_test1" token_3.abi = "abi_test1" - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) resp = client.get(self.apiurl, headers={"issuer-address": issuer_address_1}) @@ -758,13 +768,14 @@ def test_normal_6(self, mock_get, client, db): "is_offering": False, "tradable_exchange_contract_address": "0x1234567890abCdFe1234567890ABCdFE12345678", "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "contact_information": "contactInformation_test1", "privacy_policy": "privacyPolicy_test1", "issue_datetime": _issue_datetime_1, "token_status": 1, "transfer_approval_required": True, "memo": "memo_test1", - "contract_version": TokenVersion.V_23_12, + "contract_version": TokenVersion.V_24_06, }, { "issuer_address": token_2.issuer_address, @@ -803,13 +814,14 @@ def test_normal_6(self, mock_get, client, db): "is_offering": False, "tradable_exchange_contract_address": "0x1234567890AbcdfE1234567890abcdfE12345680", "personal_info_contract_address": "0x1234567890abcdFE1234567890ABcdfE12345681", + "require_personal_info_registered": True, "contact_information": "contactInformation_test2", "privacy_policy": "privacyPolicy_test2", "issue_datetime": _issue_datetime_2, "token_status": 0, "transfer_approval_required": False, "memo": "memo_test2", - "contract_version": TokenVersion.V_23_12, + "contract_version": TokenVersion.V_24_06, }, ] diff --git a/tests/test_app_routers_bond_tokens_POST.py b/tests/app/test_bond_tokens_POST.py similarity index 98% rename from tests/test_app_routers_bond_tokens_POST.py rename to tests/app/test_bond_tokens_POST.py index adf569b5..af6f7df9 100644 --- a/tests/test_app_routers_bond_tokens_POST.py +++ b/tests/app/test_bond_tokens_POST.py @@ -20,13 +20,13 @@ import hashlib import random import string -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import ANY, patch import pytest from sqlalchemy import select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.exceptions import SendTransactionError @@ -48,7 +48,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) class TestAppRoutersBondTokensPOST: @@ -93,9 +93,7 @@ async def test_normal_1(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) @@ -168,7 +166,7 @@ async def test_normal_1(self, client, db): assert token_1.token_address == "contract_address_test1" assert token_1.abi == "abi_test1" assert token_1.token_status == 1 - assert token_1.version == TokenVersion.V_23_12 + assert token_1.version == TokenVersion.V_24_06 position = db.scalars(select(IDXPosition).limit(1)).first() assert position.token_address == "contract_address_test1" @@ -224,9 +222,7 @@ async def test_normal_2(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) @@ -257,6 +253,7 @@ async def test_normal_2(self, client, db): "is_redeemed": True, # update "tradable_exchange_contract_address": "0x0000000000000000000000000000000000000001", # update "personal_info_contract_address": "0x0000000000000000000000000000000000000002", # update + "require_personal_info_registered": False, # update "contact_information": "contact info test", # update "privacy_policy": "privacy policy test", # update "transfer_approval_required": True, # update @@ -310,7 +307,7 @@ async def test_normal_2(self, client, db): assert token_1.token_address == "contract_address_test1" assert token_1.abi == "abi_test1" assert token_1.token_status == 0 - assert token_1.version == TokenVersion.V_23_12 + assert token_1.version == TokenVersion.V_24_06 position = db.scalars(select(IDXPosition).limit(1)).first() assert position is None @@ -367,9 +364,7 @@ async def test_normal_3(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) @@ -442,7 +437,7 @@ async def test_normal_3(self, client, db): assert token_1.token_address == "contract_address_test1" assert token_1.abi == "abi_test1" assert token_1.token_status == 1 - assert token_1.version == TokenVersion.V_23_12 + assert token_1.version == TokenVersion.V_24_06 position = db.scalars(select(IDXPosition).limit(1)).first() assert position.token_address == "contract_address_test1" diff --git a/tests/test_app_routers_bond_tokens_{token_address}_GET.py b/tests/app/test_bond_tokens_{token_address}_GET.py similarity index 96% rename from tests/test_app_routers_bond_tokens_{token_address}_GET.py rename to tests/app/test_bond_tokens_{token_address}_GET.py index 18a8b21f..77e0a7b2 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_GET.py @@ -46,7 +46,7 @@ def test_normal_1(self, mock_get, client, db): token.issuer_address = "issuer_address_test1" token.token_address = "token_address_test1" token.abi = "abi_test1" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -87,6 +87,7 @@ def test_normal_1(self, mock_get, client, db): mock_token.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token.require_personal_info_registered = True mock_token.interest_payment_date = [ "interestPaymentDate1_test1", "interestPaymentDate2_test1", @@ -147,13 +148,14 @@ def test_normal_1(self, mock_get, client, db): "is_offering": False, "tradable_exchange_contract_address": "0x1234567890abCdFe1234567890ABCdFE12345678", "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "contact_information": "contactInformation_test1", "privacy_policy": "privacyPolicy_test1", "issue_datetime": _issue_datetime, "token_status": 1, "transfer_approval_required": True, "memo": "memo_test1", - "contract_version": TokenVersion.V_23_12, + "contract_version": TokenVersion.V_24_06, } assert resp.status_code == 200 @@ -170,7 +172,7 @@ def test_normal_2(self, mock_get, client, db): token.issuer_address = "issuer_address_test1" token.token_address = "token_address_test1" token.abi = "abi_test1" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -211,6 +213,7 @@ def test_normal_2(self, mock_get, client, db): mock_token.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token.require_personal_info_registered = True mock_token.interest_payment_date = [ "interestPaymentDate1_test1", "interestPaymentDate2_test1", @@ -271,13 +274,14 @@ def test_normal_2(self, mock_get, client, db): "is_offering": False, "tradable_exchange_contract_address": "0x1234567890abCdFe1234567890ABCdFE12345678", "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "contact_information": "contactInformation_test1", "privacy_policy": "privacyPolicy_test1", "issue_datetime": _issue_datetime, "token_status": 1, "transfer_approval_required": True, "memo": "memo_test1", - "contract_version": TokenVersion.V_23_12, + "contract_version": TokenVersion.V_24_06, } assert resp.status_code == 200 @@ -309,7 +313,7 @@ def test_error_2(self, client, db): token.token_address = "token_address_test1" token.abi = "abi_test1" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_POST.py b/tests/app/test_bond_tokens_{token_address}_POST.py similarity index 92% rename from tests/test_app_routers_bond_tokens_{token_address}_POST.py rename to tests/app/test_bond_tokens_{token_address}_POST.py index 5c0402af..a32af693 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_POST.py +++ b/tests/app/test_bond_tokens_{token_address}_POST.py @@ -25,7 +25,7 @@ from eth_keyfile import decode_keyfile_json from sqlalchemy import select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.exceptions import SendTransactionError @@ -45,7 +45,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) async def deploy_bond_token_contract( @@ -110,7 +110,7 @@ async def test_normal_1_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -131,6 +131,7 @@ async def test_normal_1_1(self, client, db): "is_redeemed": True, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", "transfer_approval_required": True, @@ -175,6 +176,7 @@ async def test_normal_1_1(self, client, db): "privacy_policy": "", "status": True, "personal_info_contract_address": "0x0000000000000000000000000000000000000000", + "require_personal_info_registered": True, "transferable": False, "is_offering": False, "transfer_approval_required": False, @@ -208,6 +210,7 @@ async def test_normal_1_1(self, client, db): "is_redeemed": True, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", "transfer_approval_required": True, @@ -246,7 +249,7 @@ async def test_normal_1_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -267,6 +270,7 @@ async def test_normal_1_2(self, client, db): "is_redeemed": True, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", "transfer_approval_required": True, @@ -311,6 +315,7 @@ async def test_normal_1_2(self, client, db): "privacy_policy": "", "status": True, "personal_info_contract_address": "0x0000000000000000000000000000000000000000", + "require_personal_info_registered": True, "transferable": False, "is_offering": False, "transfer_approval_required": False, @@ -344,6 +349,7 @@ async def test_normal_1_2(self, client, db): "is_redeemed": True, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", "transfer_approval_required": True, @@ -382,7 +388,7 @@ async def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -450,7 +456,7 @@ async def test_normal_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -515,6 +521,7 @@ async def test_normal_3(self, client, db): "privacy_policy": "", "status": True, "personal_info_contract_address": "0x0000000000000000000000000000000000000000", + "require_personal_info_registered": True, "transferable": False, "is_offering": False, "transfer_approval_required": False, @@ -1228,7 +1235,7 @@ def test_error_7(self, IbetStraightBondContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # mock @@ -1350,7 +1357,7 @@ def test_error_10(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -1398,7 +1405,7 @@ def test_error_11(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -1420,3 +1427,97 @@ def test_error_11(self, client, db): "meta": {"code": 2, "title": "SendTransactionError"}, "detail": "failed to send transaction", } + + # + # OperationNotSupportedVersionError: v23.12 + def test_error_12_1(self, client, db): + test_account = config_eth_account("user1") + _issuer_address = test_account["address"] + _keyfile = test_account["keyfile_json"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + + # prepare data + account = Account() + account.issuer_address = _issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.token_status = 1 + token.version = TokenVersion.V_22_12 + db.add(token) + + db.commit() + + # request target API + req_param = { + "base_fx_rate": 10.0, + } + resp = client.post( + self.base_url.format(_token_address), + json=req_param, + headers={ + "issuer-address": _issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 6, "title": "OperationNotSupportedVersionError"}, + "detail": "the operation is not supported in 22_12", + } + + # + # OperationNotSupportedVersionError: v24.6 + def test_error_12_2(self, client, db): + test_account = config_eth_account("user1") + _issuer_address = test_account["address"] + _keyfile = test_account["keyfile_json"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + + # prepare data + account = Account() + account.issuer_address = _issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.token_status = 1 + token.version = TokenVersion.V_23_12 + db.add(token) + + db.commit() + + # request target API + req_param = { + "require_personal_info_registered": True, + } + resp = client.post( + self.base_url.format(_token_address), + json=req_param, + headers={ + "issuer-address": _issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 6, "title": "OperationNotSupportedVersionError"}, + "detail": "the operation is not supported in 23_12", + } diff --git a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_GET.py b/tests/app/test_bond_tokens_{token_address}_additional_issue_GET.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_additional_issue_GET.py rename to tests/app/test_bond_tokens_{token_address}_additional_issue_GET.py index b6478bc5..cf900881 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_additional_issue_GET.py @@ -74,7 +74,7 @@ def test_normal_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -110,7 +110,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -191,7 +191,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -278,7 +278,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -365,7 +365,7 @@ def test_error_2(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -425,7 +425,7 @@ def test_error_4_1(self, client, db): "detail": [ { "ctx": {"expected": "0 or 1"}, - "input": -1, + "input": "-1", "loc": ["query", "sort_order"], "msg": "Input should be 0 or 1", "type": "enum", @@ -449,7 +449,7 @@ def test_error_4_2(self, client, db): "detail": [ { "ctx": {"expected": "0 or 1"}, - "input": 2, + "input": "2", "loc": ["query", "sort_order"], "msg": "Input should be 0 or 1", "type": "enum", diff --git a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_POST.py b/tests/app/test_bond_tokens_{token_address}_additional_issue_POST.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_additional_issue_POST.py rename to tests/app/test_bond_tokens_{token_address}_additional_issue_POST.py index f922ff8e..fedbc5d7 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_POST.py +++ b/tests/app/test_bond_tokens_{token_address}_additional_issue_POST.py @@ -58,7 +58,7 @@ def test_normal_1(self, IbetStraightBondContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -115,7 +115,7 @@ def test_normal_2(self, IbetStraightBondContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -163,7 +163,7 @@ def test_error_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -210,7 +210,7 @@ def test_error_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -257,7 +257,7 @@ def test_error_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -400,7 +400,7 @@ def test_error_7(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -514,7 +514,7 @@ def test_error_10(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -562,7 +562,7 @@ def test_error_11(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_GET.py b/tests/app/test_bond_tokens_{token_address}_additional_issue_batch_GET.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_GET.py rename to tests/app/test_bond_tokens_{token_address}_additional_issue_batch_GET.py index ec0ced69..b464a9f2 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_additional_issue_batch_GET.py @@ -52,7 +52,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # request target API @@ -78,7 +78,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -127,7 +127,7 @@ def test_normal_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -244,7 +244,7 @@ def test_normal_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -348,7 +348,7 @@ def test_normal_3_3(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -458,7 +458,7 @@ def test_normal_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -560,7 +560,7 @@ def test_normal_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -683,7 +683,7 @@ def test_error_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_POST.py b/tests/app/test_bond_tokens_{token_address}_additional_issue_batch_POST.py similarity index 97% rename from tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_POST.py rename to tests/app/test_bond_tokens_{token_address}_additional_issue_batch_POST.py index 39420237..1402f7e0 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_POST.py +++ b/tests/app/test_bond_tokens_{token_address}_additional_issue_batch_POST.py @@ -66,7 +66,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -131,7 +131,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -206,7 +206,7 @@ def test_error_1_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -258,7 +258,7 @@ def test_error_1_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -313,7 +313,7 @@ def test_error_1_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -368,7 +368,7 @@ def test_error_1_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -423,7 +423,7 @@ def test_error_1_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -472,7 +472,7 @@ def test_error_1_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -521,7 +521,7 @@ def test_error_1_6(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -568,7 +568,7 @@ def test_error_1_7_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -615,7 +615,7 @@ def test_error_1_7_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -703,7 +703,7 @@ def test_error_1_8_2(self, client, db): token.token_address = token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py b/tests/app/test_bond_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py rename to tests/app/test_bond_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py index e85984a6..77c99428 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py @@ -71,7 +71,7 @@ def test_normal_1_1(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -158,7 +158,7 @@ def test_normal_1_2(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -245,7 +245,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -358,7 +358,7 @@ def test_error_1(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_history_GET.py b/tests/app/test_bond_tokens_{token_address}_history_GET.py similarity index 96% rename from tests/test_app_routers_bond_tokens_{token_address}_history_GET.py rename to tests/app/test_bond_tokens_{token_address}_history_GET.py index 80523de8..41345770 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_history_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_history_GET.py @@ -28,7 +28,7 @@ from starlette.testclient import TestClient from web3 import Web3 from web3.contract import Contract -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.model.blockchain import IbetStraightBondContract @@ -46,7 +46,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) async def deploy_bond_token_contract( @@ -94,6 +94,7 @@ async def deploy_bond_token_contract( is_offering=True, # update tradable_exchange_contract_address=tradable_exchange_contract_address, # update personal_info_contract_address=personal_info_contract_address, # update + require_personal_info_registered=False, # update image_url=None, contact_information="contact info test", # update privacy_policy="privacy policy test", # update @@ -157,6 +158,10 @@ async def deploy_bond_token_contract( token_create_param["personal_info_contract_address"] ).build_transaction(build_tx_param) ContractUtils.send_transaction(transaction=tx, private_key=private_key) + tx = contract.functions.setRequirePersonalInfoRegistered( + token_create_param["require_personal_info_registered"] + ).build_transaction(build_tx_param) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) tx = contract.functions.setContactInformation( token_create_param["contact_information"] ).build_transaction(build_tx_param) @@ -272,7 +277,7 @@ def test_normal_1(self, client, db, personal_info_contract): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -325,7 +330,7 @@ async def test_normal_2(self, client, db, personal_info_contract): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -416,7 +421,7 @@ async def test_normal_3_1(self, client, db, personal_info_contract): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -505,7 +510,7 @@ async def test_normal_3_2(self, client, db, personal_info_contract): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -585,7 +590,7 @@ async def test_normal_3_3(self, client, db, personal_info_contract, monkeypatch) _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _operation_log_1 = TokenUpdateOperationLog() @@ -622,7 +627,7 @@ async def test_normal_3_3(self, client, db, personal_info_contract, monkeypatch) resp = client.get( self.base_url.format(_token_address), params={ - "created_from": str(datetime(2023, 5, 3, 8, 0, 0)), + "created_from": "2023-05-03 08:00:00", }, ) @@ -644,13 +649,13 @@ async def test_normal_3_3(self, client, db, personal_info_contract, monkeypatch) "original_contents": {}, "modified_contents": {"memo": "20230504"}, "operation_category": TokenUpdateOperationCategory.UPDATE.value, - "created": ANY, + "created": "2023-05-04T09:00:00+09:00", }, { "original_contents": {}, "modified_contents": {"memo": "20230503"}, "operation_category": TokenUpdateOperationCategory.UPDATE.value, - "created": ANY, + "created": "2023-05-03T09:00:00+09:00", }, ], } @@ -690,7 +695,7 @@ async def test_normal_3_4(self, client, db, personal_info_contract, monkeypatch) _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _operation_log_1 = TokenUpdateOperationLog() @@ -727,7 +732,7 @@ async def test_normal_3_4(self, client, db, personal_info_contract, monkeypatch) resp = client.get( self.base_url.format(_token_address), params={ - "created_to": str(datetime(2023, 5, 2, 0, 0, 0)), + "created_to": "2023-05-02 00:00:00", }, ) @@ -745,7 +750,7 @@ async def test_normal_3_4(self, client, db, personal_info_contract, monkeypatch) "original_contents": None, "modified_contents": create_param, "operation_category": TokenUpdateOperationCategory.ISSUE.value, - "created": ANY, + "created": "2023-05-01T09:00:00+09:00", }, ], } @@ -781,7 +786,7 @@ async def test_normal_4_1(self, client, db, personal_info_contract): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -876,7 +881,7 @@ async def test_normal_4_2(self, client, db, personal_info_contract): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -972,7 +977,7 @@ async def test_normal_5_1(self, client, db, personal_info_contract): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1052,7 +1057,7 @@ async def test_normal_5_2(self, client, db, personal_info_contract): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1109,39 +1114,37 @@ def test_error_1(self, client, db): "meta": {"code": 1, "title": "RequestValidationError"}, "detail": [ { - "ctx": {"expected": "'Issue' or 'Update'"}, - "input": "test", + "type": "enum", "loc": ["query", "operation_category"], "msg": "Input should be 'Issue' or 'Update'", - "type": "enum", + "input": "test", + "ctx": {"expected": "'Issue' or 'Update'"}, }, { - "ctx": {"expected": "'created' or 'operation_category'"}, - "input": "test", + "type": "enum", "loc": ["query", "sort_item"], "msg": "Input should be 'created' or 'operation_category'", - "type": "enum", + "input": "test", + "ctx": {"expected": "'created' or 'operation_category'"}, }, { - "input": "test", + "type": "enum", "loc": ["query", "sort_order"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", - "type": "int_parsing", + "msg": "Input should be 0 or 1", + "input": "test", + "ctx": {"expected": "0 or 1"}, }, { - "input": "test", - "loc": ["query", "offset"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", "type": "int_parsing", + "loc": ["query", "offset"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "test", }, { - "input": "test", - "loc": ["query", "limit"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", "type": "int_parsing", + "loc": ["query", "limit"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "test", }, ], } diff --git a/tests/test_app_routers_bond_tokens_{token_address}_holders_GET.py b/tests/app/test_bond_tokens_{token_address}_holders_GET.py similarity index 85% rename from tests/test_app_routers_bond_tokens_{token_address}_holders_GET.py rename to tests/app/test_bond_tokens_{token_address}_holders_GET.py index 50d4854b..e7d53cb1 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_holders_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_holders_GET.py @@ -57,7 +57,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -95,7 +95,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: Position @@ -212,7 +212,7 @@ def test_normal_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -441,7 +441,7 @@ def test_normal_4_1_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -576,7 +576,7 @@ def test_normal_4_1_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -733,7 +733,7 @@ def test_normal_4_2_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -925,7 +925,7 @@ def test_normal_4_2_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1136,7 +1136,7 @@ def test_normal_4_2_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1347,7 +1347,7 @@ def test_normal_4_3_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1539,7 +1539,7 @@ def test_normal_4_3_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1750,7 +1750,7 @@ def test_normal_4_3_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1961,7 +1961,7 @@ def test_normal_4_4_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2153,7 +2153,7 @@ def test_normal_4_4_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2353,6 +2353,7 @@ def test_normal_4_4_3(self, client, db): _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + _account_address_4 = "0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec" account = Account() account.issuer_address = _issuer_address @@ -2364,7 +2365,7 @@ def test_normal_4_4_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2500,6 +2501,17 @@ def test_normal_4_4_3(self, client, db): } db.add(idx_personal_info_3) + # prepare data: account_address_4 + idx_position_4 = IDXPosition() + idx_position_4.token_address = _token_address + idx_position_4.account_address = _account_address_4 + idx_position_4.balance = 99 + idx_position_4.exchange_balance = 99 + idx_position_4.exchange_commitment = 99 + idx_position_4.pending_transfer = 99 + idx_position_4.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_4) + db.commit() # request target API @@ -2512,7 +2524,7 @@ def test_normal_4_4_3(self, client, db): # assertion assert resp.status_code == 200 assert resp.json() == { - "result_set": {"count": 2, "total": 3, "offset": None, "limit": None}, + "result_set": {"count": 3, "limit": None, "offset": None, "total": 4}, "holders": [ { "account_address": _account_address_1, @@ -2545,19 +2557,642 @@ def test_normal_4_4_3(self, client, db): "is_corporate": None, "tax_category": None, }, - "balance": 20, - "exchange_balance": 21, - "exchange_commitment": 22, - "pending_transfer": 10, - "locked": 20, - "modified": "2023-10-24T02:20:00", + "balance": 20, + "exchange_balance": 21, + "exchange_commitment": 22, + "pending_transfer": 10, + "locked": 20, + "modified": "2023-10-24T02:20:00", + }, + { + "account_address": _account_address_4, + "pending_transfer": 99, + "personal_information": { + "address": None, + "birth": None, + "email": None, + "is_corporate": None, + "key_manager": None, + "name": None, + "postal_code": None, + "tax_category": None, + }, + "balance": 99, + "exchange_balance": 99, + "exchange_commitment": 99, + "locked": 0, + "modified": "2023-10-24T03:00:00", + }, + ], + } + + # + # Search filter: balance + pending_transfer & "=" + def test_normal_4_5_1(self, client, db): + user = config_eth_account("user1") + _issuer_address = user["address"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" + _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" + _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + + account = Account() + account.issuer_address = _issuer_address + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + # prepare data: account_address_1 + idx_position_1 = IDXPosition() + idx_position_1.token_address = _token_address + idx_position_1.account_address = _account_address_1 + idx_position_1.balance = 10 + idx_position_1.exchange_balance = 11 + idx_position_1.exchange_commitment = 12 + idx_position_1.pending_transfer = 5 + idx_position_1.modified = datetime(2023, 10, 24, 0, 0, 0) + db.add(idx_position_1) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 10, 0) + db.add(idx_locked_position) + + idx_personal_info_1 = IDXPersonalInfo() + idx_personal_info_1.account_address = _account_address_1 + idx_personal_info_1.issuer_address = _issuer_address + idx_personal_info_1.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(idx_personal_info_1) + + # prepare data: account_address_2 + idx_position_2 = IDXPosition() + idx_position_2.token_address = _token_address + idx_position_2.account_address = _account_address_2 + idx_position_2.balance = 20 + idx_position_2.exchange_balance = 21 + idx_position_2.exchange_commitment = 22 + idx_position_2.pending_transfer = 10 + idx_position_2.modified = datetime(2023, 10, 24, 2, 0, 0) + db.add(idx_position_2) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 10, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 20, 0) + db.add(idx_locked_position) + + # prepare data: account_address_3 + idx_position_3 = IDXPosition() + idx_position_3.token_address = _token_address + idx_position_3.account_address = _account_address_3 + idx_position_3.balance = 99 + idx_position_3.exchange_balance = 99 + idx_position_3.exchange_commitment = 99 + idx_position_3.pending_transfer = 99 + idx_position_3.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_3) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 4, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 5, 0, 0) + db.add(idx_locked_position) + + # Other locked position + _locked_position = IDXLockedPosition() + _locked_position.token_address = "other_token_address" + _locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + _locked_position.account_address = _account_address_1 + _locked_position.value = 5 + _locked_position.modified = datetime(2023, 10, 25, 0, 2, 0) + db.add(_locked_position) + + idx_personal_info_3 = IDXPersonalInfo() + idx_personal_info_3.account_address = _account_address_3 + idx_personal_info_3.issuer_address = _issuer_address + idx_personal_info_3.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + # PersonalInfo is partially registered. + } + db.add(idx_personal_info_3) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(_token_address), + headers={"issuer-address": _issuer_address}, + params={ + "balance_and_pending_transfer": 30, + "balance_and_pending_transfer_operator": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "total": 3, "offset": None, "limit": None}, + "holders": [ + { + "account_address": _account_address_2, + "personal_information": { + "key_manager": None, + "name": None, + "postal_code": None, + "address": None, + "email": None, + "birth": None, + "is_corporate": None, + "tax_category": None, + }, + "balance": 20, + "exchange_balance": 21, + "exchange_commitment": 22, + "pending_transfer": 10, + "locked": 20, + "modified": "2023-10-24T02:20:00", + }, + ], + } + + # + # Search filter: balance + pending_transfer & ">=" + def test_normal_4_5_2(self, client, db): + user = config_eth_account("user1") + _issuer_address = user["address"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" + _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" + _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + + account = Account() + account.issuer_address = _issuer_address + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + # prepare data: account_address_1 + idx_position_1 = IDXPosition() + idx_position_1.token_address = _token_address + idx_position_1.account_address = _account_address_1 + idx_position_1.balance = 10 + idx_position_1.exchange_balance = 11 + idx_position_1.exchange_commitment = 12 + idx_position_1.pending_transfer = 5 + idx_position_1.modified = datetime(2023, 10, 24, 0, 0, 0) + db.add(idx_position_1) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 10, 0) + db.add(idx_locked_position) + + idx_personal_info_1 = IDXPersonalInfo() + idx_personal_info_1.account_address = _account_address_1 + idx_personal_info_1.issuer_address = _issuer_address + idx_personal_info_1.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(idx_personal_info_1) + + # prepare data: account_address_2 + idx_position_2 = IDXPosition() + idx_position_2.token_address = _token_address + idx_position_2.account_address = _account_address_2 + idx_position_2.balance = 20 + idx_position_2.exchange_balance = 21 + idx_position_2.exchange_commitment = 22 + idx_position_2.pending_transfer = 10 + idx_position_2.modified = datetime(2023, 10, 24, 2, 0, 0) + db.add(idx_position_2) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 10, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 20, 0) + db.add(idx_locked_position) + + # prepare data: account_address_3 + idx_position_3 = IDXPosition() + idx_position_3.token_address = _token_address + idx_position_3.account_address = _account_address_3 + idx_position_3.balance = 99 + idx_position_3.exchange_balance = 99 + idx_position_3.exchange_commitment = 99 + idx_position_3.pending_transfer = 99 + idx_position_3.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_3) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 4, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 5, 0, 0) + db.add(idx_locked_position) + + # Other locked position + _locked_position = IDXLockedPosition() + _locked_position.token_address = "other_token_address" + _locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + _locked_position.account_address = _account_address_1 + _locked_position.value = 5 + _locked_position.modified = datetime(2023, 10, 25, 0, 2, 0) + db.add(_locked_position) + + idx_personal_info_3 = IDXPersonalInfo() + idx_personal_info_3.account_address = _account_address_3 + idx_personal_info_3.issuer_address = _issuer_address + idx_personal_info_3.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + # PersonalInfo is partially registered. + } + db.add(idx_personal_info_3) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(_token_address), + headers={"issuer-address": _issuer_address}, + params={ + "balance_and_pending_transfer": 30, + "balance_and_pending_transfer_operator": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "total": 3, "offset": None, "limit": None}, + "holders": [ + { + "account_address": _account_address_2, + "personal_information": { + "key_manager": None, + "name": None, + "postal_code": None, + "address": None, + "email": None, + "birth": None, + "is_corporate": None, + "tax_category": None, + }, + "balance": 20, + "exchange_balance": 21, + "exchange_commitment": 22, + "pending_transfer": 10, + "locked": 20, + "modified": "2023-10-24T02:20:00", + }, + { + "account_address": _account_address_3, + "personal_information": { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": None, + "tax_category": None, + }, + "balance": 99, + "exchange_balance": 99, + "exchange_commitment": 99, + "pending_transfer": 99, + "locked": 30, + "modified": "2023-10-24T05:00:00", + }, + ], + } + + # + # Search filter: balance + pending_transfer & "<=" + def test_normal_4_5_3(self, client, db): + user = config_eth_account("user1") + _issuer_address = user["address"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" + _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" + _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + + account = Account() + account.issuer_address = _issuer_address + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + # prepare data: account_address_1 + idx_position_1 = IDXPosition() + idx_position_1.token_address = _token_address + idx_position_1.account_address = _account_address_1 + idx_position_1.balance = 10 + idx_position_1.exchange_balance = 11 + idx_position_1.exchange_commitment = 12 + idx_position_1.pending_transfer = 5 + idx_position_1.modified = datetime(2023, 10, 24, 0, 0, 0) + db.add(idx_position_1) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 10, 0) + db.add(idx_locked_position) + + idx_personal_info_1 = IDXPersonalInfo() + idx_personal_info_1.account_address = _account_address_1 + idx_personal_info_1.issuer_address = _issuer_address + idx_personal_info_1.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(idx_personal_info_1) + + # prepare data: account_address_2 + idx_position_2 = IDXPosition() + idx_position_2.token_address = _token_address + idx_position_2.account_address = _account_address_2 + idx_position_2.balance = 20 + idx_position_2.exchange_balance = 21 + idx_position_2.exchange_commitment = 22 + idx_position_2.pending_transfer = 10 + idx_position_2.modified = datetime(2023, 10, 24, 2, 0, 0) + db.add(idx_position_2) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 10, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 20, 0) + db.add(idx_locked_position) + + # prepare data: account_address_3 + idx_position_3 = IDXPosition() + idx_position_3.token_address = _token_address + idx_position_3.account_address = _account_address_3 + idx_position_3.balance = 99 + idx_position_3.exchange_balance = 99 + idx_position_3.exchange_commitment = 99 + idx_position_3.pending_transfer = 99 + idx_position_3.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_3) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 4, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 5, 0, 0) + db.add(idx_locked_position) + + # Other locked position + _locked_position = IDXLockedPosition() + _locked_position.token_address = "other_token_address" + _locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + _locked_position.account_address = _account_address_1 + _locked_position.value = 5 + _locked_position.modified = datetime(2023, 10, 25, 0, 2, 0) + db.add(_locked_position) + + idx_personal_info_3 = IDXPersonalInfo() + idx_personal_info_3.account_address = _account_address_3 + idx_personal_info_3.issuer_address = _issuer_address + idx_personal_info_3.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + # PersonalInfo is partially registered. + } + db.add(idx_personal_info_3) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(_token_address), + headers={"issuer-address": _issuer_address}, + params={ + "balance_and_pending_transfer": 20, + "balance_and_pending_transfer_operator": 2, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "total": 3, "offset": None, "limit": None}, + "holders": [ + { + "account_address": _account_address_1, + "personal_information": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "balance": 10, + "exchange_balance": 11, + "exchange_commitment": 12, + "pending_transfer": 5, + "locked": 10, + "modified": "2023-10-24T01:10:00", }, ], } - # + # # Search filter: holder_name - def test_normal_4_5(self, client, db): + def test_normal_4_6(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" @@ -2575,7 +3210,7 @@ def test_normal_4_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2747,9 +3382,9 @@ def test_normal_4_5(self, client, db): ], } - # + # # Search filter: key_manager - def test_normal_4_6(self, client, db): + def test_normal_4_7(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" @@ -2767,7 +3402,7 @@ def test_normal_4_6(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2958,9 +3593,9 @@ def test_normal_4_6(self, client, db): ], } - # + # # Search filter: account_address - def test_normal_4_7(self, client, db): + def test_normal_4_8(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" @@ -2978,7 +3613,7 @@ def test_normal_4_7(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -3170,7 +3805,7 @@ def test_normal_5_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -3403,7 +4038,7 @@ def test_normal_5_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -3636,7 +4271,7 @@ def test_normal_5_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -3869,7 +4504,7 @@ def test_normal_5_4(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -4102,7 +4737,7 @@ def test_normal_5_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -4316,7 +4951,7 @@ def test_normal_5_5(self, client, db): } # - # Sort Item: holder_name + # Sort Item: balance + pending_transfer def test_normal_5_6(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] @@ -4324,6 +4959,239 @@ def test_normal_5_6(self, client, db): _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + + account = Account() + account.issuer_address = _issuer_address + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + # prepare data: account_address_1 + idx_position_1 = IDXPosition() + idx_position_1.token_address = _token_address + idx_position_1.account_address = _account_address_1 + idx_position_1.balance = 10 + idx_position_1.exchange_balance = 11 + idx_position_1.exchange_commitment = 12 + idx_position_1.pending_transfer = 5 + idx_position_1.created = datetime(2023, 10, 24, 0, 0, 0) + idx_position_1.modified = datetime(2023, 10, 24, 0, 0, 0) + db.add(idx_position_1) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 10, 0) + db.add(idx_locked_position) + + idx_personal_info_1 = IDXPersonalInfo() + idx_personal_info_1.account_address = _account_address_1 + idx_personal_info_1.issuer_address = _issuer_address + idx_personal_info_1.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(idx_personal_info_1) + + # prepare data: account_address_2 + idx_position_2 = IDXPosition() + idx_position_2.token_address = _token_address + idx_position_2.account_address = _account_address_2 + idx_position_2.balance = 20 + idx_position_2.exchange_balance = 21 + idx_position_2.exchange_commitment = 22 + idx_position_2.pending_transfer = 10 + idx_position_2.created = datetime(2023, 10, 24, 2, 0, 0) + idx_position_2.modified = datetime(2023, 10, 24, 2, 0, 0) + db.add(idx_position_2) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 10, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 20, 0) + db.add(idx_locked_position) + + # prepare data: account_address_3 + idx_position_3 = IDXPosition() + idx_position_3.token_address = _token_address + idx_position_3.account_address = _account_address_3 + idx_position_3.balance = 99 + idx_position_3.exchange_balance = 99 + idx_position_3.exchange_commitment = 99 + idx_position_3.pending_transfer = 99 + idx_position_3.created = datetime(2023, 10, 24, 3, 0, 0) + idx_position_3.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_3) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 4, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 5, 0, 0) + db.add(idx_locked_position) + + # Other locked position + _locked_position = IDXLockedPosition() + _locked_position.token_address = "other_token_address" + _locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + _locked_position.account_address = _account_address_1 + _locked_position.value = 5 + _locked_position.modified = datetime(2023, 10, 25, 0, 2, 0) + db.add(_locked_position) + + idx_personal_info_3 = IDXPersonalInfo() + idx_personal_info_3.account_address = _account_address_3 + idx_personal_info_3.issuer_address = _issuer_address + idx_personal_info_3.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + # PersonalInfo is partially registered. + } + db.add(idx_personal_info_3) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(_token_address), + headers={"issuer-address": _issuer_address}, + params={"sort_order": 1, "sort_item": "balance_and_pending_transfer"}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "total": 3, "offset": None, "limit": None}, + "holders": [ + { + "account_address": _account_address_3, + "personal_information": { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": None, + "tax_category": None, + }, + "balance": 99, + "exchange_balance": 99, + "exchange_commitment": 99, + "pending_transfer": 99, + "locked": 30, + "modified": "2023-10-24T05:00:00", + }, + { + "account_address": _account_address_2, + "personal_information": { + "key_manager": None, + "name": None, + "postal_code": None, + "address": None, + "email": None, + "birth": None, + "is_corporate": None, + "tax_category": None, + }, + "balance": 20, + "exchange_balance": 21, + "exchange_commitment": 22, + "pending_transfer": 10, + "locked": 20, + "modified": "2023-10-24T02:20:00", + }, + { + "account_address": _account_address_1, + "personal_information": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "balance": 10, + "exchange_balance": 11, + "exchange_commitment": 12, + "pending_transfer": 5, + "locked": 10, + "modified": "2023-10-24T01:10:00", + }, + ], + } + + # + # Sort Item: holder_name + def test_normal_5_7(self, client, db): + user = config_eth_account("user1") + _issuer_address = user["address"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" + _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" + _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" _account_address_4 = "0x917eFFaC072dcda308e2337636f562D0A96F42eA" account = Account() @@ -4336,7 +5204,7 @@ def test_normal_5_6(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -4580,9 +5448,9 @@ def test_normal_5_6(self, client, db): ], } - # + # # Sort Item: key_manager - def test_normal_5_7(self, client, db): + def test_normal_5_8(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" @@ -4601,7 +5469,7 @@ def test_normal_5_7(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -4865,7 +5733,7 @@ def test_normal_6_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -5076,7 +5944,7 @@ def test_normal_6_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -5322,7 +6190,7 @@ def test_error_4(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_holders_count_GET.py b/tests/app/test_bond_tokens_{token_address}_holders_count_GET.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_holders_count_GET.py rename to tests/app/test_bond_tokens_{token_address}_holders_count_GET.py index a29a3a35..03699c35 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_holders_count_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_holders_count_GET.py @@ -55,7 +55,7 @@ def test_normal_1_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -90,7 +90,7 @@ def test_normal_1_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -134,7 +134,7 @@ def test_normal_1_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -196,7 +196,7 @@ def test_normal_1_4(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -250,7 +250,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -417,7 +417,7 @@ def test_error_4(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_holders_{account_address}_GET.py b/tests/app/test_bond_tokens_{token_address}_holders_{account_address}_GET.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_holders_{account_address}_GET.py rename to tests/app/test_bond_tokens_{token_address}_holders_{account_address}_GET.py index b062cc38..341e5fd3 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_holders_{account_address}_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_holders_{account_address}_GET.py @@ -60,7 +60,7 @@ def test_normal_1_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: Personal Info @@ -130,7 +130,7 @@ def test_normal_1_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: Position @@ -211,7 +211,7 @@ def test_normal_1_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: Position @@ -311,7 +311,7 @@ def test_normal_2_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -373,7 +373,7 @@ def test_normal_2_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -529,7 +529,7 @@ def test_error_4(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_personal_info_POST.py b/tests/app/test_bond_tokens_{token_address}_personal_info_POST.py similarity index 99% rename from tests/test_app_routers_bond_tokens_{token_address}_personal_info_POST.py rename to tests/app/test_bond_tokens_{token_address}_personal_info_POST.py index b671a987..f490e214 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_personal_info_POST.py +++ b/tests/app/test_bond_tokens_{token_address}_personal_info_POST.py @@ -59,7 +59,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -140,7 +140,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -227,7 +227,7 @@ def test_normal_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -680,7 +680,7 @@ def test_error_4(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -738,7 +738,7 @@ def test_error_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_GET.py b/tests/app/test_bond_tokens_{token_address}_personal_info_batch_GET.py similarity index 97% rename from tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_GET.py rename to tests/app/test_bond_tokens_{token_address}_personal_info_batch_GET.py index 9f918f2c..5da89c3e 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_personal_info_batch_GET.py @@ -61,7 +61,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -94,7 +94,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload @@ -153,7 +153,7 @@ def test_normal_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload @@ -208,7 +208,7 @@ def test_normal_4(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload @@ -263,7 +263,7 @@ def test_normal_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload @@ -327,7 +327,7 @@ def test_error_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -370,7 +370,7 @@ def test_error_2(self, client, db): token.issuer_address = _issuer_address_2 token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload diff --git a/tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_POST.py b/tests/app/test_bond_tokens_{token_address}_personal_info_batch_POST.py similarity index 99% rename from tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_POST.py rename to tests/app/test_bond_tokens_{token_address}_personal_info_batch_POST.py index 72dee6f2..f642c94d 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_POST.py +++ b/tests/app/test_bond_tokens_{token_address}_personal_info_batch_POST.py @@ -70,7 +70,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -147,7 +147,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -735,7 +735,7 @@ def test_error_4_1(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -797,7 +797,7 @@ def test_error_4_2(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 1 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py b/tests/app/test_bond_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py similarity index 99% rename from tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py rename to tests/app/test_bond_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py index 2e09c6d2..5321210c 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py @@ -95,7 +95,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload diff --git a/tests/test_app_routers_bond_tokens_{token_address}_redeem_GET.py b/tests/app/test_bond_tokens_{token_address}_redeem_GET.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_redeem_GET.py rename to tests/app/test_bond_tokens_{token_address}_redeem_GET.py index 6a072af6..cf2b8c9f 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_redeem_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_redeem_GET.py @@ -74,7 +74,7 @@ def test_normal_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -110,7 +110,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -191,7 +191,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -278,7 +278,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -365,7 +365,7 @@ def test_error_2(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -425,7 +425,7 @@ def test_error_4_1(self, client, db): "detail": [ { "ctx": {"expected": "0 or 1"}, - "input": -1, + "input": "-1", "loc": ["query", "sort_order"], "msg": "Input should be 0 or 1", "type": "enum", @@ -449,7 +449,7 @@ def test_error_4_2(self, client, db): "detail": [ { "ctx": {"expected": "0 or 1"}, - "input": 2, + "input": "2", "loc": ["query", "sort_order"], "msg": "Input should be 0 or 1", "type": "enum", diff --git a/tests/test_app_routers_bond_tokens_{token_address}_redeem_POST.py b/tests/app/test_bond_tokens_{token_address}_redeem_POST.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_redeem_POST.py rename to tests/app/test_bond_tokens_{token_address}_redeem_POST.py index 9b801f74..5c71938b 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_redeem_POST.py +++ b/tests/app/test_bond_tokens_{token_address}_redeem_POST.py @@ -58,7 +58,7 @@ def test_normal_1(self, IbetStraightBondContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -113,7 +113,7 @@ def test_normal_2(self, IbetStraightBondContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -159,7 +159,7 @@ def test_error_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -206,7 +206,7 @@ def test_error_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -253,7 +253,7 @@ def test_error_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -396,7 +396,7 @@ def test_error_7(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -510,7 +510,7 @@ def test_error_10(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -558,7 +558,7 @@ def test_error_11(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_GET.py b/tests/app/test_bond_tokens_{token_address}_redeem_batch_GET.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_GET.py rename to tests/app/test_bond_tokens_{token_address}_redeem_batch_GET.py index f5a55b8b..347dd656 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_redeem_batch_GET.py @@ -52,7 +52,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -80,7 +80,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -127,7 +127,7 @@ def test_normal_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -234,7 +234,7 @@ def test_normal_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -328,7 +328,7 @@ def test_normal_3_3(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -428,7 +428,7 @@ def test_normal_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -520,7 +520,7 @@ def test_normal_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -643,7 +643,7 @@ def test_error_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_POST.py b/tests/app/test_bond_tokens_{token_address}_redeem_batch_POST.py similarity index 97% rename from tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_POST.py rename to tests/app/test_bond_tokens_{token_address}_redeem_batch_POST.py index 4d10199d..57a84673 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_POST.py +++ b/tests/app/test_bond_tokens_{token_address}_redeem_batch_POST.py @@ -66,7 +66,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -131,7 +131,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -206,7 +206,7 @@ def test_error_1_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -258,7 +258,7 @@ def test_error_1_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -313,7 +313,7 @@ def test_error_1_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -368,7 +368,7 @@ def test_error_1_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -423,7 +423,7 @@ def test_error_1_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -472,7 +472,7 @@ def test_error_1_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -521,7 +521,7 @@ def test_error_1_6(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -568,7 +568,7 @@ def test_error_1_7_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -615,7 +615,7 @@ def test_error_1_7_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -703,7 +703,7 @@ def test_error_1_8_2(self, client, db): token.token_address = token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_{batch_id}_GET.py b/tests/app/test_bond_tokens_{token_address}_redeem_batch_{batch_id}_GET.py similarity index 98% rename from tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_{batch_id}_GET.py rename to tests/app/test_bond_tokens_{token_address}_redeem_batch_{batch_id}_GET.py index e23f181a..5a0f2b72 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_redeem_batch_{batch_id}_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_redeem_batch_{batch_id}_GET.py @@ -71,7 +71,7 @@ def test_normal_1_1(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -158,7 +158,7 @@ def test_normal_1_2(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -245,7 +245,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -358,7 +358,7 @@ def test_error_1(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_GET.py b/tests/app/test_bond_tokens_{token_address}_scheduled_events_GET.py similarity index 93% rename from tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_GET.py rename to tests/app/test_bond_tokens_{token_address}_scheduled_events_GET.py index 474b8d6d..0aa2e2c1 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_scheduled_events_GET.py @@ -18,9 +18,9 @@ """ import uuid -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta -from pytz import timezone +import pytz from app.model.db import ScheduledEvents, ScheduledEventType, TokenType from config import TZ @@ -30,7 +30,7 @@ class TestAppRoutersBondTokensTokenAddressScheduledEventsGET: # target API endpoint base_url = "/bond/tokens/{}/scheduled_events" - local_tz = timezone(TZ) + local_tz = pytz.timezone(TZ) ########################################################################### # Normal Case @@ -45,9 +45,9 @@ def test_normal_1(self, client, db): _token_address = "token_address_test" # prepare data - datetime_now_utc = datetime.utcnow() + datetime_now_utc = datetime.now(UTC).replace(tzinfo=None) datetime_now_str = ( - timezone("UTC") + pytz.timezone("UTC") .localize(datetime_now_utc) .astimezone(self.local_tz) .isoformat() @@ -117,9 +117,9 @@ def test_normal_2(self, client, db): # prepare data datetime_list = [] - datetime_utc = datetime.utcnow() + timedelta(hours=1) + datetime_utc = datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1) datetime_list.append(datetime_utc) - datetime_utc = datetime.utcnow() + datetime_utc = datetime.now(UTC).replace(tzinfo=None) datetime_list.append(datetime_utc.replace(tzinfo=None)) uuid_list = [str(uuid.uuid4()), str(uuid.uuid4())] @@ -167,14 +167,14 @@ def test_normal_2(self, client, db): "scheduled_event_id": uuid_list[0], "token_address": _token_address, "token_type": TokenType.IBET_STRAIGHT_BOND.value, - "scheduled_datetime": timezone("UTC") + "scheduled_datetime": pytz.timezone("UTC") .localize(datetime_list[0]) .astimezone(self.local_tz) .isoformat(), "event_type": ScheduledEventType.UPDATE.value, "status": 0, "data": update_data, - "created": timezone("UTC") + "created": pytz.timezone("UTC") .localize(datetime_list[0]) .astimezone(self.local_tz) .isoformat(), @@ -183,14 +183,14 @@ def test_normal_2(self, client, db): "scheduled_event_id": uuid_list[1], "token_address": _token_address, "token_type": TokenType.IBET_STRAIGHT_BOND.value, - "scheduled_datetime": timezone("UTC") + "scheduled_datetime": pytz.timezone("UTC") .localize(datetime_list[1]) .astimezone(self.local_tz) .isoformat(), "event_type": ScheduledEventType.UPDATE.value, "status": 0, "data": update_data, - "created": timezone("UTC") + "created": pytz.timezone("UTC") .localize(datetime_list[1]) .astimezone(self.local_tz) .isoformat(), diff --git a/tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_POST.py b/tests/app/test_bond_tokens_{token_address}_scheduled_events_POST.py similarity index 81% rename from tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_POST.py rename to tests/app/test_bond_tokens_{token_address}_scheduled_events_POST.py index d2dcbaf8..6ea29a5e 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_POST.py +++ b/tests/app/test_bond_tokens_{token_address}_scheduled_events_POST.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime, timezone +from datetime import UTC, datetime from pytz import timezone as tz from sqlalchemy import and_, select @@ -63,13 +63,13 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() # test data - datetime_now_utc = datetime.now(timezone.utc) # utc + datetime_now_utc = datetime.now(UTC) # utc datetime_now_str = datetime_now_utc.isoformat() update_data = { "face_value": 10000, @@ -86,6 +86,7 @@ def test_normal_1(self, client, db): "is_redeemed": True, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", "transfer_approval_required": True, @@ -149,7 +150,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -172,6 +173,7 @@ def test_normal_2(self, client, db): "is_redeemed": True, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", "transfer_approval_required": True, @@ -208,7 +210,7 @@ def test_normal_2(self, client, db): assert resp_1.json() == {"scheduled_event_id": _scheduled_event.event_id} assert _scheduled_event.token_type == TokenType.IBET_STRAIGHT_BOND.value assert _scheduled_event.scheduled_datetime == datetime_now_jst.astimezone( - timezone.utc + UTC ).replace(tzinfo=None) assert _scheduled_event.event_type == ScheduledEventType.UPDATE.value assert _scheduled_event.status == 0 @@ -226,7 +228,7 @@ def test_error_1_1(self, client, db): _token_address = "token_address_test" # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -271,7 +273,7 @@ def test_error_1_2(self, client, db): _token_address = "token_address_test" # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {"is_redeemed": False} @@ -312,7 +314,7 @@ def test_error_2(self, client, db): _token_address = "token_address_test" # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = { "face_value": 10000, @@ -360,13 +362,13 @@ def test_error_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -409,7 +411,7 @@ def test_error_4(self, client, db): db.commit() # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -506,13 +508,13 @@ def test_error_6(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() # test data - datetime_now_utc = datetime.now(timezone.utc) # utc + datetime_now_utc = datetime.now(UTC) # utc datetime_now_str = datetime_now_utc.isoformat() update_data = { "face_value": 10000, @@ -525,6 +527,7 @@ def test_error_6(self, client, db): "is_redeemed": True, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", "memo": "memo_test1", @@ -551,3 +554,115 @@ def test_error_6(self, client, db): "meta": {"code": 1, "title": "InvalidParameterError"}, "detail": "this token is temporarily unavailable", } + + # + # OperationNotSupportedVersionError: v23.12 + def test_error_7_1(self, client, db): + test_account = config_eth_account("user1") + _issuer_address = test_account["address"] + _keyfile = test_account["keyfile_json"] + _token_address = "token_address_test" + + # prepare data + account = Account() + account.issuer_address = _issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.token_status = 1 + token.version = TokenVersion.V_22_12 + db.add(token) + + db.commit() + + # test data + datetime_now_utc = datetime.now(UTC) + datetime_now_str = datetime_now_utc.isoformat() + update_data = { + "base_fx_rate": 10.0, + } + + # request target API + req_param = { + "scheduled_datetime": datetime_now_str, + "event_type": "Update", + "data": update_data, + } + resp = client.post( + self.base_url.format(_token_address), + json=req_param, + headers={ + "issuer-address": _issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 6, "title": "OperationNotSupportedVersionError"}, + "detail": "the operation is not supported in 22_12", + } + + # + # OperationNotSupportedVersionError: v24.6 + def test_error_7_2(self, client, db): + test_account = config_eth_account("user1") + _issuer_address = test_account["address"] + _keyfile = test_account["keyfile_json"] + _token_address = "token_address_test" + + # prepare data + account = Account() + account.issuer_address = _issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.token_status = 1 + token.version = TokenVersion.V_23_12 + db.add(token) + + db.commit() + + # test data + datetime_now_utc = datetime.now(UTC) + datetime_now_str = datetime_now_utc.isoformat() + update_data = { + "require_personal_info_registered": False, + } + + # request target API + req_param = { + "scheduled_datetime": datetime_now_str, + "event_type": "Update", + "data": update_data, + } + resp = client.post( + self.base_url.format(_token_address), + json=req_param, + headers={ + "issuer-address": _issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 6, "title": "OperationNotSupportedVersionError"}, + "detail": "the operation is not supported in 23_12", + } diff --git a/tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py b/tests/app/test_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py similarity index 97% rename from tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py rename to tests/app/test_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py index c0d757ba..96fbe341 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py +++ b/tests/app/test_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py @@ -18,9 +18,9 @@ """ import uuid -from datetime import datetime +from datetime import UTC, datetime -from pytz import timezone +import pytz from sqlalchemy import select from app.model.db import Account, ScheduledEvents, ScheduledEventType, TokenType @@ -32,7 +32,7 @@ class TestAppRoutersBondTokensTokenAddressScheduledEventsScheduledEventIdDELETE: # target API endpoint base_url = "/bond/tokens/{}/scheduled_events/{}" - local_tz = timezone(TZ) + local_tz = pytz.timezone(TZ) ########################################################################### # Normal Case @@ -54,9 +54,9 @@ def test_normal_1(self, client, db): db.commit() - datetime_now_utc = datetime.utcnow() + datetime_now_utc = datetime.now(UTC).replace(tzinfo=None) datetime_now_str = ( - timezone("UTC") + pytz.timezone("UTC") .localize(datetime_now_utc) .astimezone(self.local_tz) .isoformat() diff --git a/tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py b/tests/app/test_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py similarity index 95% rename from tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py rename to tests/app/test_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py index 419b0a9e..c82eb281 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py +++ b/tests/app/test_bond_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py @@ -18,9 +18,9 @@ """ import uuid -from datetime import datetime +from datetime import UTC, datetime -from pytz import timezone +import pytz from app.model.db import ScheduledEvents, ScheduledEventType, TokenType from config import TZ @@ -30,7 +30,7 @@ class TestAppRoutersBondTokensTokenAddressScheduledEventsScheduledEventIdGET: # target API endpoint base_url = "/bond/tokens/{}/scheduled_events/{}" - local_tz = timezone(TZ) + local_tz = pytz.timezone(TZ) ########################################################################### # Normal Case @@ -45,9 +45,9 @@ def test_normal_1(self, client, db): _token_address = "token_address_test" # prepare data - datetime_now_utc = datetime.utcnow() + datetime_now_utc = datetime.now(UTC).replace(tzinfo=None) datetime_now_str = ( - timezone("UTC") + pytz.timezone("UTC") .localize(datetime_now_utc) .astimezone(self.local_tz) .isoformat() @@ -113,9 +113,9 @@ def test_normal_2(self, client, db): _token_address = "token_address_test" # prepare data - datetime_now_utc = datetime.utcnow() + datetime_now_utc = datetime.now(UTC).replace(tzinfo=None) datetime_now_str = ( - timezone("UTC") + pytz.timezone("UTC") .localize(datetime_now_utc) .astimezone(self.local_tz) .isoformat() diff --git a/tests/test_app_routers_bond_transfer_approvals_GET.py b/tests/app/test_bond_transfer_approvals_GET.py similarity index 98% rename from tests/test_app_routers_bond_transfer_approvals_GET.py rename to tests/app/test_bond_transfer_approvals_GET.py index 79538449..b04ece78 100644 --- a/tests/test_app_routers_bond_transfer_approvals_GET.py +++ b/tests/app/test_bond_transfer_approvals_GET.py @@ -100,7 +100,7 @@ def test_normal_1(self, client, db): _token.token_address = self.test_token_address_1 _token.abi = {} _token.token_status = 2 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(share) @@ -110,7 +110,7 @@ def test_normal_1(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_2 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(failed token) @@ -207,7 +207,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_1 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(ApplyFor(unapproved)) @@ -392,7 +392,7 @@ def test_normal_3_1(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_1 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -402,7 +402,7 @@ def test_normal_3_1(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_2 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-2) @@ -412,7 +412,7 @@ def test_normal_3_1(self, client, db): _token.issuer_address = self.test_issuer_address_2 _token.token_address = self.test_token_address_3 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(issuer-1 token-1) @@ -579,7 +579,7 @@ def test_normal_3_2(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_1 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -589,7 +589,7 @@ def test_normal_3_2(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_2 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-2) @@ -599,7 +599,7 @@ def test_normal_3_2(self, client, db): _token.issuer_address = self.test_issuer_address_2 _token.token_address = self.test_token_address_3 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(issuer-1 token-1) @@ -774,7 +774,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_1 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -784,7 +784,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_2 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -794,7 +794,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_3 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -804,7 +804,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_4 _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(issuer-1 token-1) diff --git a/tests/test_app_routers_bond_transfer_approvals_{token_address}_GET.py b/tests/app/test_bond_transfer_approvals_{token_address}_GET.py similarity index 99% rename from tests/test_app_routers_bond_transfer_approvals_{token_address}_GET.py rename to tests/app/test_bond_transfer_approvals_{token_address}_GET.py index f84689cf..9774fcb7 100644 --- a/tests/test_app_routers_bond_transfer_approvals_{token_address}_GET.py +++ b/tests/app/test_bond_transfer_approvals_{token_address}_GET.py @@ -110,7 +110,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -136,7 +136,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -330,7 +330,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -450,7 +450,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -654,7 +654,7 @@ def test_normal_4_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -767,7 +767,7 @@ def test_normal_4_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -881,7 +881,7 @@ def test_normal_4_3_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -1027,7 +1027,7 @@ def test_normal_4_3_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -1173,7 +1173,7 @@ def test_normal_4_3_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -1407,7 +1407,7 @@ def test_normal_4_3_4(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -1627,7 +1627,7 @@ def test_normal_4_3_5(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -1879,7 +1879,7 @@ def test_normal_5_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2079,7 +2079,7 @@ def test_normal_5_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2319,7 +2319,7 @@ def test_normal_5_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2559,7 +2559,7 @@ def test_normal_5_4(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2764,7 +2764,7 @@ def test_normal_5_5(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2967,7 +2967,7 @@ def test_normal_5_6(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -3206,7 +3206,7 @@ def test_normal_5_7(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -3452,7 +3452,7 @@ def test_normal_5_8(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -3694,7 +3694,7 @@ def test_normal_5_9(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -4132,25 +4132,23 @@ def test_error_1_1(self, client, db): "meta": {"code": 1, "title": "RequestValidationError"}, "detail": [ { - "input": "a", + "type": "enum", "loc": ["query", "status", 0], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", - "type": "int_parsing", + "msg": "Input should be 0, 1, 2 or 3", + "input": "a", + "ctx": {"expected": "0, 1, 2 or 3"}, }, { - "input": "c", - "loc": ["query", "offset"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", "type": "int_parsing", + "loc": ["query", "offset"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "c", }, { - "input": "d", - "loc": ["query", "limit"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", "type": "int_parsing", + "loc": ["query", "limit"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "d", }, ], } @@ -4175,7 +4173,7 @@ def test_error_1_2(self, client, db): "detail": [ { "ctx": {"expected": "0, 1, 2 or 3"}, - "input": -1, + "input": "-1", "loc": ["query", "status", 0], "msg": "Input should be 0, 1, 2 or 3", "type": "enum", @@ -4203,7 +4201,7 @@ def test_error_1_3(self, client, db): "detail": [ { "ctx": {"expected": "0, 1, 2 or 3"}, - "input": 4, + "input": "4", "loc": ["query", "status", 0], "msg": "Input should be 0, 1, 2 or 3", "type": "enum", @@ -4237,7 +4235,7 @@ def test_error_2(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_bond_transfer_approvals_{token_address}_{id}_GET.py b/tests/app/test_bond_transfer_approvals_{token_address}_{id}_GET.py similarity index 98% rename from tests/test_app_routers_bond_transfer_approvals_{token_address}_{id}_GET.py rename to tests/app/test_bond_transfer_approvals_{token_address}_{id}_GET.py index afd0da70..71fd6fbe 100644 --- a/tests/test_app_routers_bond_transfer_approvals_{token_address}_{id}_GET.py +++ b/tests/app/test_bond_transfer_approvals_{token_address}_{id}_GET.py @@ -96,7 +96,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -209,7 +209,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -274,7 +274,7 @@ def test_normal_2_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -384,7 +384,7 @@ def test_normal_2_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -496,7 +496,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -610,7 +610,7 @@ def test_normal_4_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -722,7 +722,7 @@ def test_normal_4_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -835,7 +835,7 @@ def test_normal_4_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -982,7 +982,7 @@ def test_error_2(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1010,7 +1010,7 @@ def test_error_3(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 1 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_bond_transfer_approvals_{token_address}_{id}_POST.py b/tests/app/test_bond_transfer_approvals_{token_address}_{id}_POST.py similarity index 97% rename from tests/test_app_routers_bond_transfer_approvals_{token_address}_{id}_POST.py rename to tests/app/test_bond_transfer_approvals_{token_address}_{id}_POST.py index 7638f467..5e5ed817 100644 --- a/tests/test_app_routers_bond_transfer_approvals_{token_address}_{id}_POST.py +++ b/tests/app/test_bond_transfer_approvals_{token_address}_{id}_POST.py @@ -18,12 +18,11 @@ """ import hashlib -from datetime import datetime +from datetime import UTC, datetime from unittest import mock from unittest.mock import ANY, MagicMock import pytest -from pytz import timezone from sqlalchemy import select import config @@ -49,8 +48,6 @@ from app.utils.e2ee_utils import E2EEUtils from tests.account_config import config_eth_account -local_tz = timezone(config.TZ) - class TestAppRoutersBondTransferApprovalsTokenAddressIdPOST: # target API endpoint @@ -93,7 +90,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -168,7 +165,10 @@ def test_normal_1_1(self, client, db): assert resp.status_code == 200 assert resp.json() is None - _expected = {"application_id": 100, "data": str(datetime.utcnow().timestamp())} + _expected = { + "application_id": 100, + "data": str(datetime.now(UTC).replace(tzinfo=None).timestamp()), + } mock_transfer.assert_called_once_with( data=ApproveTransferParams(**_expected), @@ -227,7 +227,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -303,7 +303,10 @@ def test_normal_1_2(self, client, db): assert resp.status_code == 200 assert resp.json() is None - _expected = {"escrow_id": 100, "data": str(datetime.utcnow().timestamp())} + _expected = { + "escrow_id": 100, + "data": str(datetime.now(UTC).replace(tzinfo=None).timestamp()), + } mock_transfer.assert_called_once_with( data=EscrowApproveTransferParams(**_expected), @@ -362,7 +365,7 @@ def test_normal_2_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -437,7 +440,10 @@ def test_normal_2_1(self, client, db): assert resp.status_code == 200 assert resp.json() is None - _expected = {"application_id": 100, "data": str(datetime.utcnow().timestamp())} + _expected = { + "application_id": 100, + "data": str(datetime.now(UTC).replace(tzinfo=None).timestamp()), + } mock_transfer.assert_called_once_with( data=CancelTransferParams(**_expected), @@ -501,7 +507,7 @@ def test_normal_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -576,7 +582,10 @@ def test_normal_3(self, client, db): assert resp.status_code == 200 assert resp.json() is None - _expected = {"application_id": 100, "data": str(datetime.utcnow().timestamp())} + _expected = { + "application_id": 100, + "data": str(datetime.now(UTC).replace(tzinfo=None).timestamp()), + } mock_transfer.assert_called_once_with( data=ApproveTransferParams(**_expected), @@ -832,7 +841,7 @@ def test_error_3_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -877,7 +886,7 @@ def test_error_4_1(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -921,7 +930,7 @@ def test_error_4_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -986,7 +995,7 @@ def test_error_4_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1051,7 +1060,7 @@ def test_error_4_4(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1114,7 +1123,7 @@ def test_error_4_5(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1177,7 +1186,7 @@ def test_error_4_6(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1253,7 +1262,7 @@ def test_error_5_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1346,7 +1355,7 @@ def test_error_5_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1456,7 +1465,7 @@ def test_error_5_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1550,7 +1559,7 @@ def test_error_5_4(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1655,7 +1664,7 @@ def test_error_6_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1748,7 +1757,7 @@ def test_error_6_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1846,7 +1855,7 @@ def test_error_7_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1914,7 +1923,7 @@ def test_error_7_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 diff --git a/tests/test_app_routers_bond_transfers_POST.py b/tests/app/test_bond_transfers_POST.py similarity index 99% rename from tests/test_app_routers_bond_transfers_POST.py rename to tests/app/test_bond_transfers_POST.py index de4bbdb8..dd9e02cd 100644 --- a/tests/test_app_routers_bond_transfers_POST.py +++ b/tests/app/test_bond_transfers_POST.py @@ -65,7 +65,7 @@ def test_normal_1(self, IbetStraightBondContract_mock, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -140,7 +140,7 @@ def test_normal_2(self, IbetStraightBondContract_mock, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -533,7 +533,7 @@ def test_error_9(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -593,7 +593,7 @@ def test_error_10(self, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/app/test_bond_transfers_{token_address}_GET.py b/tests/app/test_bond_transfers_{token_address}_GET.py new file mode 100644 index 00000000..0a414b4d --- /dev/null +++ b/tests/app/test_bond_transfers_{token_address}_GET.py @@ -0,0 +1,1950 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from datetime import datetime + +from pytz import timezone + +import config +from app.model.db import ( + IDXPersonalInfo, + IDXTransfer, + IDXTransferSourceEventType, + Token, + TokenType, + TokenVersion, +) + +local_tz = timezone(config.TZ) + + +class TestAppRoutersBondTransfersGET: + # target API endpoint + base_url = "/bond/transfers/{}" + + test_transaction_hash = "test_transaction_hash" + test_issuer_address = "test_issuer_address" + test_token_address = "test_token_address" + + test_from_address_1 = "test_from_address_1" + test_from_address_2 = "test_from_address_2" + test_to_address_1 = "test_to_address_1" + test_to_address_2 = "test_to_address_2" + + test_block_timestamp = [ + datetime.strptime("2022/01/02 15:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/03 + datetime.strptime("2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/02 + datetime.strptime("2022/01/02 00:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/02 + ] + test_block_timestamp_str = [ + "2022-01-03T00:20:30+09:00", + "2022-01-02T00:20:30+09:00", + "2022-01-02T09:20:30+09:00", + ] + + ########################################################################### + # Normal Case + ########################################################################### + + # + # default sort + def test_normal_1(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + for i in range(0, 3): + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = i + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[i] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get(self.base_url.format(self.test_token_address)) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # offset, limit + def test_normal_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_from_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_to_address_1 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + # prepare data: IDXTransfer + for i in range(0, 3): + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = i + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[i] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address) + "?offset=1&limit=1" + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": 1, "limit": 1, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": { + "address": "address_test1", + "birth": "birth_test1", + "email": "email_test1", + "is_corporate": False, + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "tax_category": 10, + }, + "to_address": self.test_to_address_1, + "to_address_personal_information": { + "address": "address_test2", + "birth": "birth_test2", + "email": "email_test2", + "is_corporate": False, + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "tax_category": 10, + }, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: block_timestamp_from + def test_normal_3_1(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"block_timestamp_from": "2022-01-02T09:20:30"}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: block_timestamp_to + def test_normal_3_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"block_timestamp_to": "2022-01-02T09:20:30"}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: from_address + def test_normal_3_3(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"from_address": self.test_from_address_1}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: to_address + def test_normal_3_4(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"to_address": self.test_to_address_1}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: from_address_name + def test_normal_3_5(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_from_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_from_address_2 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"from_address_name": "テスト太郎1"}, # test_from_address_1's name + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 2}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: to_address_name + def test_normal_3_6(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_to_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_to_address_2 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"to_address_name": "テスト太郎1"}, # test_from_address_1's name + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 2}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: amount (EQUAL) + def test_normal_3_7_1(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 10 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 20 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 30 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"amount": 20}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 20, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: amount (GTE) + def test_normal_3_7_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 10 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 20 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 30 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"amount": 20, "amount_operator": 1}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 30, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 20, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: amount (LTE) + def test_normal_3_7_3(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 10 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 20 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 30 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"amount": 20, "amount_operator": 2}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 10, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 20, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: source_event + def test_normal_3_8(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"source_event": IDXTransferSourceEventType.UNLOCK.value}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + } + ], + } + assert resp.json() == assumed_response + + # + # filter: data + def test_normal_3_9(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), params={"data": "unlo"} + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + } + ], + } + assert resp.json() == assumed_response + + # + # sort: block_timestamp ASC + def test_normal_4_1(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + for i in range(0, 3): + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = i + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[i] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "block_timestamp", + "sort_order": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: from_address ASC + def test_normal_4_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "from_address", + "sort_order": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_2, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_2, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: to_address DESC + def test_normal_4_3(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "to_address", + "sort_order": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_2, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: from_address_name DESC + def test_normal_4_4(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_from_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_from_address_2 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "from_address_name", + "sort_order": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 2}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_2, + "from_address_personal_information": { + "address": "address_test2", + "birth": "birth_test2", + "email": "email_test2", + "is_corporate": False, + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "tax_category": 10, + }, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": { + "address": "address_test1", + "birth": "birth_test1", + "email": "email_test1", + "is_corporate": False, + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "tax_category": 10, + }, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: to_address_name DESC + def test_normal_4_5(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_to_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_to_address_2 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "to_address_name", + "sort_order": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 2}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_2, + "to_address_personal_information": { + "address": "address_test2", + "birth": "birth_test2", + "email": "email_test2", + "is_corporate": False, + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "tax_category": 10, + }, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": { + "address": "address_test1", + "birth": "birth_test1", + "email": "email_test1", + "is_corporate": False, + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "tax_category": 10, + }, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: amount DESC + def test_normal_4_6(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "amount", + "sort_order": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + ########################################################################### + # Error Case + ########################################################################### + + # + # token not found + def test_error_1(self, client, db): + # request target API + resp = client.get(self.base_url.format(self.test_token_address)) + + # assertion + assert resp.status_code == 404 + assumed_response = { + "meta": {"code": 1, "title": "NotFound"}, + "detail": "token not found", + } + assert resp.json() == assumed_response + + # + # processing token + def test_error_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.token_status = 0 + _token.version = TokenVersion.V_24_06 + db.add(_token) + + db.commit() + + # request target API + resp = client.get(self.base_url.format(self.test_token_address)) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 1, "title": "InvalidParameterError"}, + "detail": "this token is temporarily unavailable", + } + + # + # param error: sort_item + def test_error_3(self, client, db): + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"sort_item": "block_timestamp12345"}, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "enum", + "loc": ["query", "sort_item"], + "msg": "Input should be 'block_timestamp', 'from_address', 'to_address', 'from_address_name', 'to_address_name' or 'amount'", + "input": "block_timestamp12345", + "ctx": { + "expected": "'block_timestamp', 'from_address', 'to_address', 'from_address_name', 'to_address_name' or 'amount'" + }, + } + ], + } + + # + # param error: sort_order(min) + def test_error_4(self, client, db): + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), params={"sort_order": -1} + ) + + # assertion + assert resp.status_code == 422 + assumed_response = { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "ctx": {"expected": "0 or 1"}, + "input": "-1", + "loc": ["query", "sort_order"], + "msg": "Input should be 0 or 1", + "type": "enum", + } + ], + } + assert resp.json() == assumed_response + + # + # param error: sort_order(max) + def test_error_5(self, client, db): + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), params={"sort_order": 2} + ) + + # assertion + assert resp.status_code == 422 + assumed_response = { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "ctx": {"expected": "0 or 1"}, + "input": "2", + "loc": ["query", "sort_order"], + "msg": "Input should be 0 or 1", + "type": "enum", + } + ], + } + assert resp.json() == assumed_response diff --git a/tests/test_app_routers_docs_GET.py b/tests/app/test_docs_GET.py similarity index 100% rename from tests/test_app_routers_docs_GET.py rename to tests/app/test_docs_GET.py diff --git a/tests/test_app_routers_e2e_messaging_accounts_GET.py b/tests/app/test_e2e_messaging_accounts_GET.py similarity index 92% rename from tests/test_app_routers_e2e_messaging_accounts_GET.py rename to tests/app/test_e2e_messaging_accounts_GET.py index 788304f4..1ec9e09e 100644 --- a/tests/test_app_routers_e2e_messaging_accounts_GET.py +++ b/tests/app/test_e2e_messaging_accounts_GET.py @@ -18,7 +18,7 @@ """ import time -from datetime import datetime +from datetime import UTC, datetime from app.model.db import E2EMessagingAccount, E2EMessagingAccountRsaKey @@ -56,7 +56,7 @@ def test_normal_2(self, client, db): _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_1" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) @@ -90,7 +90,7 @@ def test_normal_3(self, client, db): _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_1" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) @@ -110,7 +110,7 @@ def test_normal_3(self, client, db): _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000002" _rsa_key.rsa_public_key = "rsa_public_key_2_1" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) @@ -121,21 +121,21 @@ def test_normal_3(self, client, db): _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000003" _rsa_key.rsa_public_key = "rsa_public_key_3_1" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000003" _rsa_key.rsa_public_key = "rsa_public_key_3_2" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000003" _rsa_key.rsa_public_key = "rsa_public_key_3_3" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) diff --git a/tests/test_app_routers_e2e_messaging_accounts_POST.py b/tests/app/test_e2e_messaging_accounts_POST.py similarity index 98% rename from tests/test_app_routers_e2e_messaging_accounts_POST.py rename to tests/app/test_e2e_messaging_accounts_POST.py index fbb4033f..0e617eed 100644 --- a/tests/test_app_routers_e2e_messaging_accounts_POST.py +++ b/tests/app/test_e2e_messaging_accounts_POST.py @@ -18,7 +18,7 @@ """ import base64 -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest import mock from unittest.mock import ANY, MagicMock @@ -70,7 +70,7 @@ def test_normal_1(self, client, db, e2e_messaging_contract): { "number": 12345, "timestamp": datetime( - 2099, 4, 27, 12, 34, 56, tzinfo=timezone.utc + 2099, 4, 27, 12, 34, 56, tzinfo=UTC ).timestamp(), }, ], @@ -172,7 +172,7 @@ def generate_random(self, NumberOfBytes): { "number": 12345, "timestamp": datetime( - 2099, 4, 27, 12, 34, 56, tzinfo=timezone.utc + 2099, 4, 27, 12, 34, 56, tzinfo=UTC ).timestamp(), }, ], diff --git a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_DELETE.py b/tests/app/test_e2e_messaging_accounts_{account_address}_DELETE.py similarity index 93% rename from tests/test_app_routers_e2e_messaging_accounts_{account_address}_DELETE.py rename to tests/app/test_e2e_messaging_accounts_{account_address}_DELETE.py index 031b2114..088099a3 100644 --- a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_DELETE.py +++ b/tests/app/test_e2e_messaging_accounts_{account_address}_DELETE.py @@ -18,7 +18,7 @@ """ import time -from datetime import datetime +from datetime import UTC, datetime from sqlalchemy import select @@ -46,21 +46,21 @@ def test_normal_1(self, client, db): _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_1" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_2" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_3" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) diff --git a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_GET.py b/tests/app/test_e2e_messaging_accounts_{account_address}_GET.py similarity index 93% rename from tests/test_app_routers_e2e_messaging_accounts_{account_address}_GET.py rename to tests/app/test_e2e_messaging_accounts_{account_address}_GET.py index f5643bf2..54ec0b3c 100644 --- a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_GET.py +++ b/tests/app/test_e2e_messaging_accounts_{account_address}_GET.py @@ -18,7 +18,7 @@ """ import time -from datetime import datetime +from datetime import UTC, datetime from app.model.db import E2EMessagingAccount, E2EMessagingAccountRsaKey @@ -43,21 +43,21 @@ def test_normal_1(self, client, db): _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_1" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_2" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_3" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) diff --git a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_eoa_password_POST.py b/tests/app/test_e2e_messaging_accounts_{account_address}_eoa_password_POST.py similarity index 100% rename from tests/test_app_routers_e2e_messaging_accounts_{account_address}_eoa_password_POST.py rename to tests/app/test_e2e_messaging_accounts_{account_address}_eoa_password_POST.py diff --git a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_rsa_key_POST.py b/tests/app/test_e2e_messaging_accounts_{account_address}_rsa_key_POST.py similarity index 94% rename from tests/test_app_routers_e2e_messaging_accounts_{account_address}_rsa_key_POST.py rename to tests/app/test_e2e_messaging_accounts_{account_address}_rsa_key_POST.py index 2ca489cc..8b9e1025 100644 --- a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_rsa_key_POST.py +++ b/tests/app/test_e2e_messaging_accounts_{account_address}_rsa_key_POST.py @@ -18,7 +18,7 @@ """ import time -from datetime import datetime +from datetime import UTC, datetime from sqlalchemy import select @@ -43,21 +43,21 @@ def test_normal_1(self, client, db): _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_1" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_2" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_3" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) @@ -99,21 +99,21 @@ def test_normal_2(self, client, db): _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_1" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_2" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) _rsa_key = E2EMessagingAccountRsaKey() _rsa_key.account_address = "0x1234567890123456789012345678900000000000" _rsa_key.rsa_public_key = "rsa_public_key_1_3" - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) diff --git a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_rsa_passphrase_POST.py b/tests/app/test_e2e_messaging_accounts_{account_address}_rsa_passphrase_POST.py similarity index 97% rename from tests/test_app_routers_e2e_messaging_accounts_{account_address}_rsa_passphrase_POST.py rename to tests/app/test_e2e_messaging_accounts_{account_address}_rsa_passphrase_POST.py index bb598a3f..84428bba 100644 --- a/tests/test_app_routers_e2e_messaging_accounts_{account_address}_rsa_passphrase_POST.py +++ b/tests/app/test_e2e_messaging_accounts_{account_address}_rsa_passphrase_POST.py @@ -18,7 +18,7 @@ """ import time -from datetime import datetime +from datetime import UTC, datetime from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA @@ -122,7 +122,7 @@ def test_normal_1(self, client, db, e2e_messaging_contract): _rsa_key.account_address = user_address_1 _rsa_key.rsa_private_key = "rsa_private_key_1_1" _rsa_key.rsa_passphrase = E2EEUtils.encrypt("password_1") - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) @@ -130,7 +130,7 @@ def test_normal_1(self, client, db, e2e_messaging_contract): _rsa_key.account_address = user_address_1 _rsa_key.rsa_private_key = "rsa_private_key_1_2" _rsa_key.rsa_passphrase = E2EEUtils.encrypt("password_2") - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) @@ -138,7 +138,7 @@ def test_normal_1(self, client, db, e2e_messaging_contract): _rsa_key.account_address = user_address_1 _rsa_key.rsa_private_key = self.rsa_private_key _rsa_key.rsa_passphrase = E2EEUtils.encrypt(old_passphrase) - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) time.sleep(1) @@ -359,7 +359,7 @@ def test_normal_3(self, client, db, e2e_messaging_contract): _rsa_key.account_address = user_address_1 _rsa_key.rsa_private_key = self.rsa_private_key _rsa_key.rsa_passphrase = E2EEUtils.encrypt(old_passphrase) - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) db.commit() @@ -400,7 +400,7 @@ def test_normal_4(self, client, db, e2e_messaging_contract): _rsa_key.account_address = user_address_1 _rsa_key.rsa_private_key = self.rsa_private_key _rsa_key.rsa_passphrase = E2EEUtils.encrypt(old_passphrase) - _rsa_key.block_timestamp = datetime.utcnow() + _rsa_key.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key) db.commit() diff --git a/tests/test_app_routers_e2e_messaging_messages_GET.py b/tests/app/test_e2e_messaging_messages_GET.py similarity index 100% rename from tests/test_app_routers_e2e_messaging_messages_GET.py rename to tests/app/test_e2e_messaging_messages_GET.py diff --git a/tests/test_app_routers_e2e_messaging_messages_{id}_GET.py b/tests/app/test_e2e_messaging_messages_{id}_GET.py similarity index 100% rename from tests/test_app_routers_e2e_messaging_messages_{id}_GET.py rename to tests/app/test_e2e_messaging_messages_{id}_GET.py diff --git a/tests/test_app_routers_e2ee_GET.py b/tests/app/test_e2ee_GET.py similarity index 100% rename from tests/test_app_routers_e2ee_GET.py rename to tests/app/test_e2ee_GET.py diff --git a/tests/test_app_routers_files_GET.py b/tests/app/test_files_GET.py similarity index 100% rename from tests/test_app_routers_files_GET.py rename to tests/app/test_files_GET.py diff --git a/tests/test_app_routers_files_POST.py b/tests/app/test_files_POST.py similarity index 100% rename from tests/test_app_routers_files_POST.py rename to tests/app/test_files_POST.py diff --git a/tests/test_app_routers_files_{file_id}_DELETE.py b/tests/app/test_files_{file_id}_DELETE.py similarity index 100% rename from tests/test_app_routers_files_{file_id}_DELETE.py rename to tests/app/test_files_{file_id}_DELETE.py diff --git a/tests/test_app_routers_files_{file_id}_GET.py b/tests/app/test_files_{file_id}_GET.py similarity index 100% rename from tests/test_app_routers_files_{file_id}_GET.py rename to tests/app/test_files_{file_id}_GET.py diff --git a/tests/test_app_routers_freezelog_ChangeFreezeLogAccountPassword.py b/tests/app/test_freezelog_ChangeFreezeLogAccountPassword.py similarity index 100% rename from tests/test_app_routers_freezelog_ChangeFreezeLogAccountPassword.py rename to tests/app/test_freezelog_ChangeFreezeLogAccountPassword.py diff --git a/tests/test_app_routers_freezelog_CreateFreezeLogAccount.py b/tests/app/test_freezelog_CreateFreezeLogAccount.py similarity index 100% rename from tests/test_app_routers_freezelog_CreateFreezeLogAccount.py rename to tests/app/test_freezelog_CreateFreezeLogAccount.py diff --git a/tests/test_app_routers_freezelog_DeleteFreezeLogAccount.py b/tests/app/test_freezelog_DeleteFreezeLogAccount.py similarity index 100% rename from tests/test_app_routers_freezelog_DeleteFreezeLogAccount.py rename to tests/app/test_freezelog_DeleteFreezeLogAccount.py diff --git a/tests/test_app_routers_freezelog_ListAllFreezeLogAccount.py b/tests/app/test_freezelog_ListAllFreezeLogAccount.py similarity index 100% rename from tests/test_app_routers_freezelog_ListAllFreezeLogAccount.py rename to tests/app/test_freezelog_ListAllFreezeLogAccount.py diff --git a/tests/test_app_routers_freezelog_RecordNewFreezeLog.py b/tests/app/test_freezelog_RecordNewFreezeLog.py similarity index 100% rename from tests/test_app_routers_freezelog_RecordNewFreezeLog.py rename to tests/app/test_freezelog_RecordNewFreezeLog.py diff --git a/tests/test_app_routers_freezelog_RetriveFreezeLog.py b/tests/app/test_freezelog_RetriveFreezeLog.py similarity index 100% rename from tests/test_app_routers_freezelog_RetriveFreezeLog.py rename to tests/app/test_freezelog_RetriveFreezeLog.py diff --git a/tests/test_app_routers_freezelog_UpdateFreezeLog.py b/tests/app/test_freezelog_UpdateFreezeLog.py similarity index 100% rename from tests/test_app_routers_freezelog_UpdateFreezeLog.py rename to tests/app/test_freezelog_UpdateFreezeLog.py diff --git a/tests/test_app_routers_healthcheck_GET.py b/tests/app/test_healthcheck_GET.py similarity index 99% rename from tests/test_app_routers_healthcheck_GET.py rename to tests/app/test_healthcheck_GET.py index bc096137..e35fa859 100644 --- a/tests/test_app_routers_healthcheck_GET.py +++ b/tests/app/test_healthcheck_GET.py @@ -92,7 +92,6 @@ def test_error_1(self, client, db): # request target api resp = client.get(self.apiurl) - print(resp.json()) # assertion assert resp.status_code == 503 assert resp.json() == { diff --git a/tests/test_app_routers_ledger_{token_address}_details_data_GET.py b/tests/app/test_ledger_{token_address}_details_data_GET.py similarity index 98% rename from tests/test_app_routers_ledger_{token_address}_details_data_GET.py rename to tests/app/test_ledger_{token_address}_details_data_GET.py index 16e117d1..5c84b770 100644 --- a/tests/test_app_routers_ledger_{token_address}_details_data_GET.py +++ b/tests/app/test_ledger_{token_address}_details_data_GET.py @@ -45,7 +45,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_data_1_1 = LedgerDetailsData() @@ -161,7 +161,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_data_1_1 = LedgerDetailsData() @@ -274,7 +274,7 @@ def test_normal_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_data_1_1 = LedgerDetailsData() @@ -416,7 +416,7 @@ def test_error_2_1(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 2 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -477,7 +477,7 @@ def test_error_3(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_ledger_{token_address}_details_data_POST.py b/tests/app/test_ledger_{token_address}_details_data_POST.py similarity index 99% rename from tests/test_app_routers_ledger_{token_address}_details_data_POST.py rename to tests/app/test_ledger_{token_address}_details_data_POST.py index 974b0fec..dc0ac3db 100644 --- a/tests/test_app_routers_ledger_{token_address}_details_data_POST.py +++ b/tests/app/test_ledger_{token_address}_details_data_POST.py @@ -46,7 +46,7 @@ def test_normal_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -118,7 +118,7 @@ def test_normal_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -413,7 +413,7 @@ def test_error_5(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_DELETE.py b/tests/app/test_ledger_{token_address}_details_data_{data_id}_DELETE.py similarity index 98% rename from tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_DELETE.py rename to tests/app/test_ledger_{token_address}_details_data_{data_id}_DELETE.py index 01febd2a..2362ea1a 100644 --- a/tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_DELETE.py +++ b/tests/app/test_ledger_{token_address}_details_data_{data_id}_DELETE.py @@ -45,7 +45,7 @@ def test_normal_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_1_data_1 = LedgerDetailsData() @@ -196,7 +196,7 @@ def test_error_4(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_GET.py b/tests/app/test_ledger_{token_address}_details_data_{data_id}_GET.py similarity index 98% rename from tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_GET.py rename to tests/app/test_ledger_{token_address}_details_data_{data_id}_GET.py index bbdfda28..14be1c94 100644 --- a/tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_GET.py +++ b/tests/app/test_ledger_{token_address}_details_data_{data_id}_GET.py @@ -44,7 +44,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_1_data_1 = LedgerDetailsData() @@ -127,7 +127,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_1_data_1 = LedgerDetailsData() @@ -207,7 +207,7 @@ def test_normal_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_1_data_1 = LedgerDetailsData() @@ -326,7 +326,7 @@ def test_error_2_1(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 2 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -381,7 +381,7 @@ def test_error_3(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_POST.py b/tests/app/test_ledger_{token_address}_details_data_{data_id}_POST.py similarity index 99% rename from tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_POST.py rename to tests/app/test_ledger_{token_address}_details_data_{data_id}_POST.py index 4f37882e..f820d3d0 100644 --- a/tests/test_app_routers_ledger_{token_address}_details_data_{data_id}_POST.py +++ b/tests/app/test_ledger_{token_address}_details_data_{data_id}_POST.py @@ -49,7 +49,7 @@ def test_normal_1(self, mock_func, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_1_data_1 = LedgerDetailsData() @@ -134,7 +134,7 @@ def test_normal_2(self, mock_func, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _details_1_data_1 = LedgerDetailsData() @@ -445,7 +445,7 @@ def test_error_5(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_ledger_{token_address}_history_GET.py b/tests/app/test_ledger_{token_address}_history_GET.py similarity index 98% rename from tests/test_app_routers_ledger_{token_address}_history_GET.py rename to tests/app/test_ledger_{token_address}_history_GET.py index b15e436e..83071bc8 100644 --- a/tests/test_app_routers_ledger_{token_address}_history_GET.py +++ b/tests/app/test_ledger_{token_address}_history_GET.py @@ -45,7 +45,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -110,7 +110,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -172,7 +172,7 @@ def test_normal_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -291,7 +291,7 @@ def test_error_2_1(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 2 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # request target API @@ -350,7 +350,7 @@ def test_error_3(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_ledger_{token_address}_history_{ledger_id}_GET.py b/tests/app/test_ledger_{token_address}_history_{ledger_id}_GET.py similarity index 60% rename from tests/test_app_routers_ledger_{token_address}_history_{ledger_id}_GET.py rename to tests/app/test_ledger_{token_address}_history_{ledger_id}_GET.py index c1e65ae9..5a6d0206 100644 --- a/tests/test_app_routers_ledger_{token_address}_history_{ledger_id}_GET.py +++ b/tests/app/test_ledger_{token_address}_history_{ledger_id}_GET.py @@ -58,7 +58,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -100,8 +100,8 @@ def test_normal_1_1(self, client, db): }, { "account_address": account_address_2, - "name": "name_test_2", - "address": "address_test_2", + "name": None, + "address": None, "amount": 100, "price": 200, "balance": 300, @@ -115,6 +115,7 @@ def test_normal_1_1(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": True, }, { "token_detail_type": "権利_test_2", @@ -152,6 +153,7 @@ def test_normal_1_1(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -172,7 +174,7 @@ def test_normal_1_1(self, client, db): db.commit() - # request target AsPI + # request target API resp = client.get( self.base_url.format(token_address=token_address, ledger_id=1), params={ @@ -221,8 +223,8 @@ def test_normal_1_1(self, client, db): }, { "account_address": account_address_2, - "name": "name_test_2", - "address": "address_test_2", + "name": None, + "address": None, "amount": 100, "price": 200, "balance": 300, @@ -236,6 +238,7 @@ def test_normal_1_1(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": True, }, { "token_detail_type": "権利_test_2", @@ -273,6 +276,7 @@ def test_normal_1_1(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -303,7 +307,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -360,6 +364,7 @@ def test_normal_1_2(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": False, }, { "token_detail_type": "権利_test_2", @@ -397,6 +402,7 @@ def test_normal_1_2(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -417,7 +423,7 @@ def test_normal_1_2(self, client, db): db.commit() - # request target AsPI + # request target API resp = client.get( self.base_url.format(token_address=token_address, ledger_id=1), params={ @@ -478,6 +484,7 @@ def test_normal_1_2(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": False, }, { "token_detail_type": "権利_test_2", @@ -515,6 +522,7 @@ def test_normal_1_2(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -529,11 +537,10 @@ def test_normal_1_2(self, client, db): ], } - # - # latest_flg = 1 (Get the latest personal info) - # address_1 has personal info in the DB - # address_2 has no personal info in the DB - def test_normal_2_1(self, client, db): + # + # latest_flg = 0 (Get the latest personal info) + # - ledger detail contains None value in "name" and "value": some_personal_info_not_registered = True + def test_normal_2(self, client, db): user_1 = config_eth_account("user1") issuer_address = user_1["address"] token_address = "0xABCdeF1234567890abcdEf123456789000000000" @@ -548,7 +555,7 @@ def test_normal_2_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -581,8 +588,8 @@ def test_normal_2_1(self, client, db): "data": [ { "account_address": account_address_1, - "name": "name_test_1", - "address": "address_test_1", + "name": None, + "address": None, "amount": 10, "price": 20, "balance": 30, @@ -605,6 +612,7 @@ def test_normal_2_1(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": True, }, { "token_detail_type": "権利_test_2", @@ -642,6 +650,7 @@ def test_normal_2_1(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -660,17 +669,6 @@ def test_normal_2_1(self, client, db): ) # JST 2022/01/02 db.add(_ledger_1) - _idx_personal_info_1 = ( - IDXPersonalInfo() - ) # Note: account_address_1 has personal information in DB - _idx_personal_info_1.account_address = account_address_1 - _idx_personal_info_1.issuer_address = issuer_address - _idx_personal_info_1.personal_info = { - "name": "name_db_1", - "address": "address_db_1", - } - db.add(_idx_personal_info_1) - _details_1 = LedgerDetailsTemplate() _details_1.token_address = token_address _details_1.token_detail_type = "権利_test_1" @@ -698,38 +696,17 @@ def test_normal_2_1(self, client, db): token = IbetStraightBondContract() token.personal_info_contract_address = personal_info_contract_address token.issuer_address = issuer_address - token_get_mock = mock.patch( - "app.model.blockchain.IbetStraightBondContract.get", return_value=token - ) - personal_get_info_mock = mock.patch( - "app.model.blockchain.PersonalInfoContract.get_info" - ) # request target API - with token_get_mock as token_get_mock_patch, personal_get_info_mock as personal_get_info_mock_patch: - # Note: - # account_address_2 has no personal information in the DB - # and gets information from the contract - personal_get_info_mock_patch.side_effect = [ - { - "name": "name_contract_2", - "address": "address_contract_2", - } - ] - - resp = client.get( - self.base_url.format(token_address=token_address, ledger_id=1), - params={ - "latest_flg": 1, - }, - headers={ - "issuer-address": issuer_address, - }, - ) - # assertion - personal_get_info_mock_patch.assert_has_calls( - [call(account_address=account_address_2, default_value=None)] - ) + resp = client.get( + self.base_url.format(token_address=token_address, ledger_id=1), + params={ + "latest_flg": 0, + }, + headers={ + "issuer-address": issuer_address, + }, + ) # assertion assert resp.status_code == 200 @@ -760,8 +737,8 @@ def test_normal_2_1(self, client, db): "data": [ { "account_address": account_address_1, - "name": "name_db_1", - "address": "address_db_1", + "name": None, + "address": None, "amount": 10, "price": 20, "balance": 30, @@ -769,8 +746,8 @@ def test_normal_2_1(self, client, db): }, { "account_address": account_address_2, - "name": "name_contract_2", - "address": "address_contract_2", + "name": "name_test_2", + "address": "address_test_2", "amount": 100, "price": 200, "balance": 300, @@ -784,6 +761,7 @@ def test_normal_2_1(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": True, }, { "token_detail_type": "権利_test_2", @@ -821,6 +799,7 @@ def test_normal_2_1(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -835,11 +814,12 @@ def test_normal_2_1(self, client, db): ], } - # + # # latest_flg = 1 (Get the latest personal info) - # address_1 has partial personal info in the DB - # address_2 has no personal info in the DB - def test_normal_2_2(self, client, db): + # - address_1 has personal info in the DB + # - address_2 has no personal info in the DB + # token.require_personal_info_registered = True + def test_normal_3_1(self, client, db): user_1 = config_eth_account("user1") issuer_address = user_1["address"] token_address = "0xABCdeF1234567890abcdEf123456789000000000" @@ -854,7 +834,7 @@ def test_normal_2_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -911,6 +891,7 @@ def test_normal_2_2(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": False, }, { "token_detail_type": "権利_test_2", @@ -948,6 +929,7 @@ def test_normal_2_2(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -968,10 +950,13 @@ def test_normal_2_2(self, client, db): _idx_personal_info_1 = ( IDXPersonalInfo() - ) # Note: account_address_1 has partial personal information in DB + ) # Note: account_address_1 has personal information in DB _idx_personal_info_1.account_address = account_address_1 _idx_personal_info_1.issuer_address = issuer_address - _idx_personal_info_1.personal_info = {"name": None, "address": None} + _idx_personal_info_1.personal_info = { + "name": "name_db_1", + "address": "address_db_1", + } db.add(_idx_personal_info_1) _details_1 = LedgerDetailsTemplate() @@ -1001,6 +986,7 @@ def test_normal_2_2(self, client, db): token = IbetStraightBondContract() token.personal_info_contract_address = personal_info_contract_address token.issuer_address = issuer_address + token.require_personal_info_registered = True token_get_mock = mock.patch( "app.model.blockchain.IbetStraightBondContract.get", return_value=token ) @@ -1063,9 +1049,8 @@ def test_normal_2_2(self, client, db): "data": [ { "account_address": account_address_1, - # Value stored with None should be converted to empty string. - "name": None, - "address": None, + "name": "name_db_1", + "address": "address_db_1", "amount": 10, "price": 20, "balance": 30, @@ -1088,6 +1073,7 @@ def test_normal_2_2(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": False, }, { "token_detail_type": "権利_test_2", @@ -1125,6 +1111,7 @@ def test_normal_2_2(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -1139,10 +1126,12 @@ def test_normal_2_2(self, client, db): ], } - # - # latest_flg = 0 (Get the latest personal info) - # ledger detail contains None value in "name" and "value" - def test_normal_3(self, client, db): + # + # latest_flg = 1 (Get the latest personal info) + # - address_1 has partial personal info in the DB + # - address_2 has no personal info in the DB + # token.require_personal_info_registered = True + def test_normal_3_2(self, client, db): user_1 = config_eth_account("user1") issuer_address = user_1["address"] token_address = "0xABCdeF1234567890abcdEf123456789000000000" @@ -1157,7 +1146,7 @@ def test_normal_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -1190,8 +1179,8 @@ def test_normal_3(self, client, db): "data": [ { "account_address": account_address_1, - "name": None, - "address": None, + "name": "name_test_1", + "address": "address_test_1", "amount": 10, "price": 20, "balance": 30, @@ -1214,6 +1203,7 @@ def test_normal_3(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": False, }, { "token_detail_type": "権利_test_2", @@ -1251,6 +1241,7 @@ def test_normal_3(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -1269,6 +1260,14 @@ def test_normal_3(self, client, db): ) # JST 2022/01/02 db.add(_ledger_1) + _idx_personal_info_1 = ( + IDXPersonalInfo() + ) # Note: account_address_1 has partial personal information in DB + _idx_personal_info_1.account_address = account_address_1 + _idx_personal_info_1.issuer_address = issuer_address + _idx_personal_info_1.personal_info = {"name": "name_test_1", "address": None} + db.add(_idx_personal_info_1) + _details_1 = LedgerDetailsTemplate() _details_1.token_address = token_address _details_1.token_detail_type = "権利_test_1" @@ -1296,17 +1295,39 @@ def test_normal_3(self, client, db): token = IbetStraightBondContract() token.personal_info_contract_address = personal_info_contract_address token.issuer_address = issuer_address + token.require_personal_info_registered = True + token_get_mock = mock.patch( + "app.model.blockchain.IbetStraightBondContract.get", return_value=token + ) + personal_get_info_mock = mock.patch( + "app.model.blockchain.PersonalInfoContract.get_info" + ) # request target API - resp = client.get( - self.base_url.format(token_address=token_address, ledger_id=1), - params={ - "latest_flg": 0, - }, - headers={ - "issuer-address": issuer_address, - }, - ) + with token_get_mock as token_get_mock_patch, personal_get_info_mock as personal_get_info_mock_patch: + # Note: + # account_address_2 has no personal information in the DB + # and gets information from the contract + personal_get_info_mock_patch.side_effect = [ + { + "name": "name_contract_2", + "address": "address_contract_2", + } + ] + + resp = client.get( + self.base_url.format(token_address=token_address, ledger_id=1), + params={ + "latest_flg": 1, + }, + headers={ + "issuer-address": issuer_address, + }, + ) + # assertion + personal_get_info_mock_patch.assert_has_calls( + [call(account_address=account_address_2, default_value=None)] + ) # assertion assert resp.status_code == 200 @@ -1337,8 +1358,7 @@ def test_normal_3(self, client, db): "data": [ { "account_address": account_address_1, - # Value stored with None should be converted to empty string. - "name": None, + "name": "name_test_1", "address": None, "amount": 10, "price": 20, @@ -1347,8 +1367,8 @@ def test_normal_3(self, client, db): }, { "account_address": account_address_2, - "name": "name_test_2", - "address": "address_test_2", + "name": "name_contract_2", + "address": "address_contract_2", "amount": 100, "price": 200, "balance": 300, @@ -1362,6 +1382,7 @@ def test_normal_3(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": False, }, { "token_detail_type": "権利_test_2", @@ -1399,6 +1420,7 @@ def test_normal_3(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -1413,14 +1435,16 @@ def test_normal_3(self, client, db): ], } - # - # Test `currency` backward compatibility - def test_normal_4(self, client, db): + # + # latest_flg = 1 (Get the latest personal info) + # token.require_personal_info_registered = False + # Personal information has not been indexed yet + def test_normal_3_3(self, client, db): user_1 = config_eth_account("user1") issuer_address = user_1["address"] token_address = "0xABCdeF1234567890abcdEf123456789000000000" account_address_1 = "0xABCdeF1234567890abCDeF123456789000000001" - account_address_2 = "0xaBcdEF1234567890aBCDEF123456789000000002" + personal_info_contract_address = "0xabcDEF1234567890AbcDEf123456789000000003" # prepare data _token = Token() @@ -1429,7 +1453,7 @@ def test_normal_4(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -1438,6 +1462,7 @@ def test_normal_4(self, client, db): _ledger_1.ledger = { "created": "2022/12/01", "token_name": "テスト原簿", + "currency": "JPY", "headers": [ { "key": "aaa", @@ -1468,15 +1493,6 @@ def test_normal_4(self, client, db): "balance": 30, "acquisition_date": "2022/12/02", }, - { - "account_address": account_address_2, - "name": "name_test_2", - "address": "address_test_2", - "amount": 100, - "price": 200, - "balance": 300, - "acquisition_date": "2022/12/03", - }, ], "footers": [ { @@ -1485,43 +1501,7 @@ def test_normal_4(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], - }, - { - "token_detail_type": "権利_test_2", - "headers": [ - { - "key": "aaa", - "value": "aaa", - }, - {"test1-1": "a", "test2-1": "b"}, - ], - "data": [ - { - "account_address": None, - "name": "name_test_1", - "address": "address_test_1", - "amount": 10, - "price": 20, - "balance": 200, - "acquisition_date": "2020/01/01", - }, - { - "account_address": None, - "name": "name_test_2", - "address": "address_test_2", - "amount": 20, - "price": 30, - "balance": 600, - "acquisition_date": "2020/01/02", - }, - ], - "footers": [ - { - "key": "aaa", - "value": "aaa", - }, - {"f-test1-1": "a", "f-test2-1": "b"}, - ], + "some_personal_info_not_registered": False, # Personal information is set (normally not possible) }, ], "footers": [ @@ -1540,25 +1520,56 @@ def test_normal_4(self, client, db): ) # JST 2022/01/02 db.add(_ledger_1) - db.commit() - - # request target AsPI - resp = client.get( - self.base_url.format(token_address=token_address, ledger_id=1), - params={ - "latest_flg": 0, + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address + _details_1.token_detail_type = "権利_test_1" + _details_1.headers = [ + { + "key": "aaa", + "value": "aaa", }, - headers={ - "issuer-address": issuer_address, + {"test1": "a", "test2": "b"}, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "aaa", }, + {"f-test1": "a", "f-test2": "b"}, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address + db.add(_details_1) + + db.commit() + + # Mock + token = IbetStraightBondContract() + token.personal_info_contract_address = personal_info_contract_address + token.issuer_address = issuer_address + token.require_personal_info_registered = False + token_get_mock = mock.patch( + "app.model.blockchain.IbetStraightBondContract.get", return_value=token ) + # request target API + with token_get_mock as token_get_mock_patch: + resp = client.get( + self.base_url.format(token_address=token_address, ledger_id=1), + params={ + "latest_flg": 1, + }, + headers={ + "issuer-address": issuer_address, + }, + ) + # assertion assert resp.status_code == 200 assert resp.json() == { "created": "2022/12/01", "token_name": "テスト原簿", - "currency": "", + "currency": "JPY", "headers": [ { "key": "aaa", @@ -1582,17 +1593,1245 @@ def test_normal_4(self, client, db): "data": [ { "account_address": account_address_1, - "name": "name_test_1", - "address": "address_test_1", + "name": None, # not null -> null + "address": None, # not null -> null "amount": 10, "price": 20, "balance": 30, "acquisition_date": "2022/12/02", }, + ], + "footers": [ { - "account_address": account_address_2, - "name": "name_test_2", - "address": "address_test_2", + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + "some_personal_info_not_registered": True, # False -> True + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + + # + # latest_flg = 1 (Get the latest personal info) + # - address_1 is issuer's address + def test_normal_3_4(self, client, db): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + token_address = "0xABCdeF1234567890abcdEf123456789000000000" + account_address_1 = "0xABCdeF1234567890abCDeF123456789000000001" + personal_info_contract_address = "0xabcDEF1234567890AbcDEf123456789000000003" + + # prepare data + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = "" + _token.issuer_address = issuer_address + _token.token_address = token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + _ledger_1 = Ledger() + _ledger_1.token_address = token_address + _ledger_1.token_type = TokenType.IBET_STRAIGHT_BOND.value + _ledger_1.ledger = { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": issuer_address, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + "some_personal_info_not_registered": False, # Personal information is set (normally not possible) + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + _ledger_1.ledger_created = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_ledger_1) + + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address + _details_1.token_detail_type = "権利_test_1" + _details_1.headers = [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address + db.add(_details_1) + + db.commit() + + # Mock + token = IbetStraightBondContract() + token.personal_info_contract_address = personal_info_contract_address + token.issuer_address = issuer_address + token.require_personal_info_registered = False + token_get_mock = mock.patch( + "app.model.blockchain.IbetStraightBondContract.get", return_value=token + ) + + # request target API + with token_get_mock as token_get_mock_patch: + resp = client.get( + self.base_url.format(token_address=token_address, ledger_id=1), + params={ + "latest_flg": 1, + }, + headers={ + "issuer-address": issuer_address, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": issuer_address, + "name": None, + "address": None, + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + "some_personal_info_not_registered": False, # Issuer cannot have any personal info + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + + # + # latest_flg = 1 (Get the latest personal info) + # - address_1 has personal info in the DB but the values are null + # - address_2 has personal info in the DB + def test_normal_3_5(self, client, db): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + token_address = "0xABCdeF1234567890abcdEf123456789000000000" + account_address_1 = "0xABCdeF1234567890abCDeF123456789000000001" + account_address_2 = "0xaBcdEF1234567890aBCDEF123456789000000002" + personal_info_contract_address = "0xabcDEF1234567890AbcDEf123456789000000003" + + # prepare data + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = "" + _token.issuer_address = issuer_address + _token.token_address = token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + _ledger_1 = Ledger() + _ledger_1.token_address = token_address + _ledger_1.token_type = TokenType.IBET_STRAIGHT_BOND.value + _ledger_1.ledger = { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": account_address_1, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + { + "account_address": account_address_2, + "name": "name_test_2", + "address": "address_test_2", + "amount": 100, + "price": 200, + "balance": 300, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + "some_personal_info_not_registered": False, + }, + { + "token_detail_type": "権利_test_2", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1-1": "a", "test2-1": "b"}, + ], + "data": [ + { + "account_address": None, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 200, + "acquisition_date": "2020/01/01", + }, + { + "account_address": None, + "name": "name_test_2", + "address": "address_test_2", + "amount": 20, + "price": 30, + "balance": 600, + "acquisition_date": "2020/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1-1": "a", "f-test2-1": "b"}, + ], + "some_personal_info_not_registered": False, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + _ledger_1.ledger_created = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_ledger_1) + + _idx_personal_info_1 = ( + IDXPersonalInfo() + ) # Note: account_address_1 has personal information in DB but the values are null + _idx_personal_info_1.account_address = account_address_1 + _idx_personal_info_1.issuer_address = issuer_address + _idx_personal_info_1.personal_info = { + "name": None, + "address": None, + } + db.add(_idx_personal_info_1) + + _idx_personal_info_2 = ( + IDXPersonalInfo() + ) # Note: account_address_2 has personal information in DB + _idx_personal_info_2.account_address = account_address_1 + _idx_personal_info_2.issuer_address = issuer_address + _idx_personal_info_2.personal_info = { + "name": "name_db_2", + "address": "address_db_2", + } + db.add(_idx_personal_info_1) + + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address + _details_1.token_detail_type = "権利_test_1" + _details_1.headers = [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address + db.add(_details_1) + + db.commit() + + # Mock + token = IbetStraightBondContract() + token.personal_info_contract_address = personal_info_contract_address + token.issuer_address = issuer_address + token.require_personal_info_registered = True + token_get_mock = mock.patch( + "app.model.blockchain.IbetStraightBondContract.get", return_value=token + ) + personal_get_info_mock = mock.patch( + "app.model.blockchain.PersonalInfoContract.get_info" + ) + + # request target API + with token_get_mock as token_get_mock_patch, personal_get_info_mock as personal_get_info_mock_patch: + # Note: + # account_address_2 has no personal information in the DB + # and gets information from the contract + personal_get_info_mock_patch.side_effect = [ + { + "name": None, + "address": None, + }, + { + "name": "name_contract_2", + "address": "address_contract_2", + }, + ] + + resp = client.get( + self.base_url.format(token_address=token_address, ledger_id=1), + params={ + "latest_flg": 1, + }, + headers={ + "issuer-address": issuer_address, + }, + ) + # assertion + personal_get_info_mock_patch.assert_has_calls( + [call(account_address=account_address_2, default_value=None)] + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": account_address_1, + "name": None, + "address": None, + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + { + "account_address": account_address_2, + "name": "name_contract_2", + "address": "address_contract_2", + "amount": 100, + "price": 200, + "balance": 300, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + "some_personal_info_not_registered": True, + }, + { + "token_detail_type": "権利_test_2", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1-1": "a", "test2-1": "b"}, + ], + "data": [ + { + "account_address": None, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 200, + "acquisition_date": "2020/01/01", + }, + { + "account_address": None, + "name": "name_test_2", + "address": "address_test_2", + "amount": 20, + "price": 30, + "balance": 600, + "acquisition_date": "2020/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1-1": "a", "f-test2-1": "b"}, + ], + "some_personal_info_not_registered": True, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + + # + # Test `currency` backward compatibility + def test_normal_4(self, client, db): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + token_address = "0xABCdeF1234567890abcdEf123456789000000000" + account_address_1 = "0xABCdeF1234567890abCDeF123456789000000001" + account_address_2 = "0xaBcdEF1234567890aBCDEF123456789000000002" + + # prepare data + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = "" + _token.issuer_address = issuer_address + _token.token_address = token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + _ledger_1 = Ledger() + _ledger_1.token_address = token_address + _ledger_1.token_type = TokenType.IBET_STRAIGHT_BOND.value + _ledger_1.ledger = { + "created": "2022/12/01", + "token_name": "テスト原簿", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": account_address_1, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + { + "account_address": account_address_2, + "name": "name_test_2", + "address": "address_test_2", + "amount": 100, + "price": 200, + "balance": 300, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + "some_personal_info_not_registered": False, + }, + { + "token_detail_type": "権利_test_2", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1-1": "a", "test2-1": "b"}, + ], + "data": [ + { + "account_address": None, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 200, + "acquisition_date": "2020/01/01", + }, + { + "account_address": None, + "name": "name_test_2", + "address": "address_test_2", + "amount": 20, + "price": 30, + "balance": 600, + "acquisition_date": "2020/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1-1": "a", "f-test2-1": "b"}, + ], + "some_personal_info_not_registered": False, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + _ledger_1.ledger_created = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_ledger_1) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(token_address=token_address, ledger_id=1), + params={ + "latest_flg": 0, + }, + headers={ + "issuer-address": issuer_address, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": account_address_1, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + { + "account_address": account_address_2, + "name": "name_test_2", + "address": "address_test_2", + "amount": 100, + "price": 200, + "balance": 300, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + "some_personal_info_not_registered": False, + }, + { + "token_detail_type": "権利_test_2", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1-1": "a", "test2-1": "b"}, + ], + "data": [ + { + "account_address": None, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 200, + "acquisition_date": "2020/01/01", + }, + { + "account_address": None, + "name": "name_test_2", + "address": "address_test_2", + "amount": 20, + "price": 30, + "balance": 600, + "acquisition_date": "2020/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1-1": "a", "f-test2-1": "b"}, + ], + "some_personal_info_not_registered": False, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + + # + # `some_personal_info_not_registered` is not set in ledger data + # Test backward compatibility for specifications earlier than v24.6 + # latest_flg != 1 + def test_normal_5_1(self, client, db): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + token_address = "0xABCdeF1234567890abcdEf123456789000000000" + account_address_1 = "0xABCdeF1234567890abCDeF123456789000000001" + account_address_2 = "0xaBcdEF1234567890aBCDEF123456789000000002" + + # prepare data + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = "" + _token.issuer_address = issuer_address + _token.token_address = token_address + _token.abi = {} + _token.version = TokenVersion.V_23_12 + db.add(_token) + + _ledger_1 = Ledger() + _ledger_1.token_address = token_address + _ledger_1.token_type = TokenType.IBET_STRAIGHT_BOND.value + _ledger_1.ledger = { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": account_address_1, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + { + "account_address": account_address_2, + "name": "name_test_2", + "address": "address_test_2", + "amount": 100, + "price": 200, + "balance": 300, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + }, + { + "token_detail_type": "権利_test_2", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1-1": "a", "test2-1": "b"}, + ], + "data": [ + { + "account_address": None, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 200, + "acquisition_date": "2020/01/01", + }, + { + "account_address": None, + "name": "name_test_2", + "address": "address_test_2", + "amount": 20, + "price": 30, + "balance": 600, + "acquisition_date": "2020/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1-1": "a", "f-test2-1": "b"}, + ], + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + _ledger_1.ledger_created = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_ledger_1) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(token_address=token_address, ledger_id=1), + params={ + "latest_flg": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": account_address_1, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + { + "account_address": account_address_2, + "name": "name_test_2", + "address": "address_test_2", + "amount": 100, + "price": 200, + "balance": 300, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + "some_personal_info_not_registered": False, + }, + { + "token_detail_type": "権利_test_2", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1-1": "a", "test2-1": "b"}, + ], + "data": [ + { + "account_address": None, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 200, + "acquisition_date": "2020/01/01", + }, + { + "account_address": None, + "name": "name_test_2", + "address": "address_test_2", + "amount": 20, + "price": 30, + "balance": 600, + "acquisition_date": "2020/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1-1": "a", "f-test2-1": "b"}, + ], + "some_personal_info_not_registered": False, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + + # + # `some_personal_info_not_registered` is not set in ledger data + # Test backward compatibility for specifications earlier than v24.6 + # latest_flg == 1 + def test_normal_5_2(self, client, db): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + token_address = "0xABCdeF1234567890abcdEf123456789000000000" + account_address_1 = "0xABCdeF1234567890abCDeF123456789000000001" + account_address_2 = "0xaBcdEF1234567890aBCDEF123456789000000002" + personal_info_contract_address = "0xabcDEF1234567890AbcDEf123456789000000003" + + # prepare data + _token = Token() + _token.type = TokenType.IBET_STRAIGHT_BOND.value + _token.tx_hash = "" + _token.issuer_address = issuer_address + _token.token_address = token_address + _token.abi = {} + _token.version = TokenVersion.V_23_12 + db.add(_token) + + _ledger_1 = Ledger() + _ledger_1.token_address = token_address + _ledger_1.token_type = TokenType.IBET_STRAIGHT_BOND.value + _ledger_1.ledger = { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": account_address_1, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + { + "account_address": account_address_2, + "name": "name_test_2", + "address": "address_test_2", + "amount": 100, + "price": 200, + "balance": 300, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1": "a", "f-test2": "b"}, + ], + }, + { + "token_detail_type": "権利_test_2", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1-1": "a", "test2-1": "b"}, + ], + "data": [ + { + "account_address": None, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 200, + "acquisition_date": "2020/01/01", + }, + { + "account_address": None, + "name": "name_test_2", + "address": "address_test_2", + "amount": 20, + "price": 30, + "balance": 600, + "acquisition_date": "2020/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"f-test1-1": "a", "f-test2-1": "b"}, + ], + }, + ], + "footers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "f-hoge": "aaaa", + "f-fuga": "bbbb", + }, + ], + } + _ledger_1.ledger_created = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_ledger_1) + + _idx_personal_info_1 = ( + IDXPersonalInfo() + ) # Note: account_address_1 has personal information in DB + _idx_personal_info_1.account_address = account_address_1 + _idx_personal_info_1.issuer_address = issuer_address + _idx_personal_info_1.personal_info = { + "name": "name_db_1", + "address": "address_db_1", + } + db.add(_idx_personal_info_1) + + db.commit() + + # Mock + token = IbetStraightBondContract() + token.personal_info_contract_address = personal_info_contract_address + token.issuer_address = issuer_address + token.require_personal_info_registered = False + token_get_mock = mock.patch( + "app.model.blockchain.IbetStraightBondContract.get", return_value=token + ) + + # request target API + with token_get_mock: + resp = client.get( + self.base_url.format(token_address=token_address, ledger_id=1), + params={ + "latest_flg": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "created": "2022/12/01", + "token_name": "テスト原簿", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + { + "hoge": "aaaa", + "fuga": "bbbb", + }, + ], + "details": [ + { + "token_detail_type": "権利_test_1", + "headers": [ + { + "key": "aaa", + "value": "aaa", + }, + {"test1": "a", "test2": "b"}, + ], + "data": [ + { + "account_address": account_address_1, + "name": "name_test_1", + "address": "address_test_1", + "amount": 10, + "price": 20, + "balance": 30, + "acquisition_date": "2022/12/02", + }, + { + "account_address": account_address_2, + "name": "name_test_2", + "address": "address_test_2", "amount": 100, "price": 200, "balance": 300, @@ -1606,6 +2845,7 @@ def test_normal_4(self, client, db): }, {"f-test1": "a", "f-test2": "b"}, ], + "some_personal_info_not_registered": False, }, { "token_detail_type": "権利_test_2", @@ -1643,6 +2883,7 @@ def test_normal_4(self, client, db): }, {"f-test1-1": "a", "f-test2-1": "b"}, ], + "some_personal_info_not_registered": False, }, ], "footers": [ @@ -1775,7 +3016,7 @@ def test_error_4_1(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 2 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1834,7 +3075,7 @@ def test_error_5(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1871,7 +3112,7 @@ def test_error_6(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1910,7 +3151,7 @@ def test_error_7(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _ledger_1 = Ledger() @@ -1986,7 +3227,7 @@ def test_error_7(self, client, db): db.commit() - # request target AsPI + # request target API with mock.patch("app.utils.fastapi_utils.RESPONSE_VALIDATION_MODE", False): resp = client.get( self.base_url.format(token_address=token_address, ledger_id=1), diff --git a/tests/test_app_routers_ledger_{token_address}_template_DELETE.py b/tests/app/test_ledger_{token_address}_template_DELETE.py similarity index 98% rename from tests/test_app_routers_ledger_{token_address}_template_DELETE.py rename to tests/app/test_ledger_{token_address}_template_DELETE.py index 3be30149..94206ff1 100644 --- a/tests/test_app_routers_ledger_{token_address}_template_DELETE.py +++ b/tests/app/test_ledger_{token_address}_template_DELETE.py @@ -51,7 +51,7 @@ def test_normal_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _template = LedgerTemplate() @@ -232,7 +232,7 @@ def test_error_4(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -266,7 +266,7 @@ def test_error_5(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_ledger_{token_address}_template_GET.py b/tests/app/test_ledger_{token_address}_template_GET.py similarity index 98% rename from tests/test_app_routers_ledger_{token_address}_template_GET.py rename to tests/app/test_ledger_{token_address}_template_GET.py index f1e9c9d4..de480c29 100644 --- a/tests/test_app_routers_ledger_{token_address}_template_GET.py +++ b/tests/app/test_ledger_{token_address}_template_GET.py @@ -50,7 +50,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _template = LedgerTemplate() @@ -215,7 +215,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _template = LedgerTemplate() @@ -377,7 +377,7 @@ def test_normal_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _template = LedgerTemplate() @@ -475,7 +475,7 @@ def test_error_2(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 2 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -528,7 +528,7 @@ def test_error_3(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -562,7 +562,7 @@ def test_error_4(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_ledger_{token_address}_template_POST.py b/tests/app/test_ledger_{token_address}_template_POST.py similarity index 99% rename from tests/test_app_routers_ledger_{token_address}_template_POST.py rename to tests/app/test_ledger_{token_address}_template_POST.py index 52a1de1f..4b1f64ba 100644 --- a/tests/test_app_routers_ledger_{token_address}_template_POST.py +++ b/tests/app/test_ledger_{token_address}_template_POST.py @@ -55,7 +55,7 @@ def test_normal_1(self, mock_func, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -247,7 +247,7 @@ def test_normal_2(self, mock_func, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _template = LedgerTemplate() @@ -906,7 +906,7 @@ def test_error_6(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1097,7 +1097,7 @@ def test_error_8(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_notifications_GET.py b/tests/app/test_notifications_GET.py similarity index 100% rename from tests/test_app_routers_notifications_GET.py rename to tests/app/test_notifications_GET.py diff --git a/tests/test_app_routers_notifications_{notice_id}_DELETE.py b/tests/app/test_notifications_{notice_id}_DELETE.py similarity index 100% rename from tests/test_app_routers_notifications_{notice_id}_DELETE.py rename to tests/app/test_notifications_{notice_id}_DELETE.py diff --git a/tests/test_app_routers_positions_{account_address}_GET.py b/tests/app/test_positions_{account_address}_GET.py similarity index 97% rename from tests/test_app_routers_positions_{account_address}_GET.py rename to tests/app/test_positions_{account_address}_GET.py index 726610f8..243844a0 100644 --- a/tests/test_app_routers_positions_{account_address}_GET.py +++ b/tests/app/test_positions_{account_address}_GET.py @@ -43,7 +43,7 @@ def test_normal_1(self, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -93,7 +93,7 @@ def test_normal_2(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -188,7 +188,7 @@ def test_normal_3_1( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position 1 @@ -227,7 +227,7 @@ def test_normal_3_1( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position-2 @@ -266,7 +266,7 @@ def test_normal_3_1( _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position-3 @@ -381,7 +381,7 @@ def test_normal_3_2( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position 1 @@ -401,7 +401,7 @@ def test_normal_3_2( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position 2 @@ -431,7 +431,7 @@ def test_normal_3_2( _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position 3 @@ -522,7 +522,7 @@ def test_normal_4( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -544,7 +544,7 @@ def test_normal_4( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -564,7 +564,7 @@ def test_normal_4( _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -645,7 +645,7 @@ def test_normal_5_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -665,7 +665,7 @@ def test_normal_5_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -685,7 +685,7 @@ def test_normal_5_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -763,7 +763,7 @@ def test_normal_5_2(self, mock_IbetShareContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -783,7 +783,7 @@ def test_normal_5_2(self, mock_IbetShareContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -803,7 +803,7 @@ def test_normal_5_2(self, mock_IbetShareContract_get, client, db): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -870,7 +870,7 @@ def test_normal_5_3( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -890,7 +890,7 @@ def test_normal_5_3( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -910,7 +910,7 @@ def test_normal_5_3( _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -930,7 +930,7 @@ def test_normal_5_3( _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -1036,7 +1036,7 @@ def test_normal_6( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -1056,7 +1056,7 @@ def test_normal_6( _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -1076,7 +1076,7 @@ def test_normal_6( _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -1096,7 +1096,7 @@ def test_normal_6( _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position diff --git a/tests/test_app_routers_positions_{account_address}_forceunlock_POST.py b/tests/app/test_positions_{account_address}_forceunlock_POST.py similarity index 98% rename from tests/test_app_routers_positions_{account_address}_forceunlock_POST.py rename to tests/app/test_positions_{account_address}_forceunlock_POST.py index a52c727c..43850152 100644 --- a/tests/test_app_routers_positions_{account_address}_forceunlock_POST.py +++ b/tests/app/test_positions_{account_address}_forceunlock_POST.py @@ -64,7 +64,7 @@ def test_normal_1(self, IbetSecurityTokenInterface_mock, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -140,7 +140,7 @@ def test_normal_2(self, IbetSecurityTokenInterface_mock, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -522,7 +522,7 @@ def test_error_3_1(self, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -626,7 +626,7 @@ def test_error_3_3(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -685,7 +685,7 @@ def test_error_4(self, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -744,7 +744,7 @@ def test_error_5(self, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_positions_{account_address}_lock_GET.py b/tests/app/test_positions_{account_address}_lock_GET.py similarity index 97% rename from tests/test_app_routers_positions_{account_address}_lock_GET.py rename to tests/app/test_positions_{account_address}_lock_GET.py index 5359cad0..3f08808b 100644 --- a/tests/test_app_routers_positions_{account_address}_lock_GET.py +++ b/tests/app/test_positions_{account_address}_lock_GET.py @@ -43,7 +43,7 @@ def test_normal_1(self, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -87,7 +87,7 @@ def test_normal_2_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _token = Token() @@ -96,7 +96,7 @@ def test_normal_2_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Locked Position @@ -186,7 +186,7 @@ def test_normal_2_2(self, mock_IbetShareContract_get, client, db): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _token = Token() @@ -195,7 +195,7 @@ def test_normal_2_2(self, mock_IbetShareContract_get, client, db): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Locked Position @@ -279,7 +279,7 @@ def test_normal_3_1(self, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Locked Position @@ -332,7 +332,7 @@ def test_normal_3_2(self, mock_IbetStraightBondContract_get, client, db): _token.tx_hash = "" _token.abi = "" _token.token_status = 2 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _token = Token() @@ -341,7 +341,7 @@ def test_normal_3_2(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Locked Position @@ -414,7 +414,7 @@ def test_normal_4(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _token = Token() @@ -423,7 +423,7 @@ def test_normal_4(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Locked Position @@ -498,7 +498,7 @@ def test_normal_5(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _token = Token() @@ -507,7 +507,7 @@ def test_normal_5(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Locked Position @@ -580,7 +580,7 @@ def test_normal_6(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Locked Position diff --git a/tests/test_app_routers_positions_{account_address}_lock_events_GET.py b/tests/app/test_positions_{account_address}_lock_events_GET.py similarity index 94% rename from tests/test_app_routers_positions_{account_address}_lock_events_GET.py rename to tests/app/test_positions_{account_address}_lock_events_GET.py index a05a0b4d..3f51494c 100644 --- a/tests/test_app_routers_positions_{account_address}_lock_events_GET.py +++ b/tests/app/test_positions_{account_address}_lock_events_GET.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime +from datetime import UTC, datetime from unittest import mock from unittest.mock import ANY @@ -45,7 +45,7 @@ def test_normal_1(self, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -88,7 +88,7 @@ def test_normal_2_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -101,7 +101,7 @@ def test_normal_2_1(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -114,7 +114,7 @@ def test_normal_2_1(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -188,7 +188,7 @@ def test_normal_2_2(self, mock_IbetShareContract_get, client, db): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -201,7 +201,7 @@ def test_normal_2_2(self, mock_IbetShareContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -214,7 +214,7 @@ def test_normal_2_2(self, mock_IbetShareContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -289,7 +289,7 @@ def test_normal_3_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -302,7 +302,7 @@ def test_normal_3_1(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = other_account_address # others _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -315,7 +315,7 @@ def test_normal_3_1(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -360,7 +360,7 @@ def test_normal_3_2(self, mock_IbetStraightBondContract_get, client, db): _token.tx_hash = "" _token.abi = "" _token.token_status = 2 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -373,7 +373,7 @@ def test_normal_3_2(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -386,7 +386,7 @@ def test_normal_3_2(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -431,7 +431,7 @@ def test_normal_4(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _token = Token() @@ -440,7 +440,7 @@ def test_normal_4(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -453,7 +453,7 @@ def test_normal_4(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -466,7 +466,7 @@ def test_normal_4(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) _lock = IDXLock() @@ -478,7 +478,7 @@ def test_normal_4(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -491,7 +491,7 @@ def test_normal_4(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -566,7 +566,7 @@ def test_normal_5_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -579,7 +579,7 @@ def test_normal_5_1(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -592,7 +592,7 @@ def test_normal_5_1(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -653,7 +653,7 @@ def test_normal_5_2(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _token = Token() @@ -662,7 +662,7 @@ def test_normal_5_2(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -675,7 +675,7 @@ def test_normal_5_2(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -688,7 +688,7 @@ def test_normal_5_2(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -749,7 +749,7 @@ def test_normal_5_3(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value # bond _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _token = Token() @@ -758,7 +758,7 @@ def test_normal_5_3(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_SHARE.value # share _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -771,7 +771,7 @@ def test_normal_5_3(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -784,7 +784,7 @@ def test_normal_5_3(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -844,7 +844,7 @@ def test_normal_5_4(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value # bond _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -858,7 +858,7 @@ def test_normal_5_4(self, mock_IbetStraightBondContract_get, client, db): _lock.recipient_address = lock_address_1 _lock.value = 1 _lock.data = {"message": "unlocked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -871,7 +871,7 @@ def test_normal_5_4(self, mock_IbetStraightBondContract_get, client, db): _lock.recipient_address = lock_address_2 _lock.value = 1 _lock.data = {"message": "unlocked_2"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) db.commit() @@ -931,7 +931,7 @@ def test_normal_5_5(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value # bond _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -944,7 +944,7 @@ def test_normal_5_5(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -956,7 +956,7 @@ def test_normal_5_5(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_2"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) db.commit() @@ -1017,7 +1017,7 @@ def test_normal_5_6(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value # bond _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -1031,7 +1031,7 @@ def test_normal_5_6(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address_1 _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) _unlock = IDXUnlock() @@ -1044,7 +1044,7 @@ def test_normal_5_6(self, mock_IbetStraightBondContract_get, client, db): _unlock.recipient_address = other_account_address_2 _unlock.value = 1 _unlock.data = {"message": "unlocked_2"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -1104,7 +1104,7 @@ def test_normal_6(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -1117,7 +1117,7 @@ def test_normal_6(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -1129,7 +1129,7 @@ def test_normal_6(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_2"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -1141,7 +1141,7 @@ def test_normal_6(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_3"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -1153,7 +1153,7 @@ def test_normal_6(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_4"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) db.commit() @@ -1257,7 +1257,7 @@ def test_normal_7(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -1270,7 +1270,7 @@ def test_normal_7(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -1282,7 +1282,7 @@ def test_normal_7(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_2"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -1294,7 +1294,7 @@ def test_normal_7(self, mock_IbetStraightBondContract_get, client, db): _lock.account_address = account_address _lock.value = 1 _lock.data = {"message": "locked_3"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) db.commit() diff --git a/tests/test_app_routers_positions_{account_address}_{token_address}_GET.py b/tests/app/test_positions_{account_address}_{token_address}_GET.py similarity index 98% rename from tests/test_app_routers_positions_{account_address}_{token_address}_GET.py rename to tests/app/test_positions_{account_address}_{token_address}_GET.py index 15f93c4a..82da5a01 100644 --- a/tests/test_app_routers_positions_{account_address}_{token_address}_GET.py +++ b/tests/app/test_positions_{account_address}_{token_address}_GET.py @@ -48,7 +48,7 @@ def test_normal_1_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -133,7 +133,7 @@ def test_normal_1_2(self, mock_IbetShareContract_get, client, db): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -216,7 +216,7 @@ def test_normal_2(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -273,7 +273,7 @@ def test_normal_3_1(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Locked Position @@ -330,7 +330,7 @@ def test_normal_3_2(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -398,7 +398,7 @@ def test_normal_3_3(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -455,7 +455,7 @@ def test_normal_3_4(self, mock_IbetStraightBondContract_get, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -578,7 +578,7 @@ def test_error_2_2(self, client, db): _token.type = TokenType.IBET_STRAIGHT_BOND.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position @@ -623,7 +623,7 @@ def test_error_3(self, client, db): _token.tx_hash = "" _token.abi = "" _token.token_status = 0 - _token.version = TokenVersion.V_23_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Position diff --git a/tests/app/test_settlement_dvp_agent_account_{account_address}_DELETE.py b/tests/app/test_settlement_dvp_agent_account_{account_address}_DELETE.py new file mode 100644 index 00000000..1c2aef7b --- /dev/null +++ b/tests/app/test_settlement_dvp_agent_account_{account_address}_DELETE.py @@ -0,0 +1,79 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from sqlalchemy import select + +from app.model.db import DVPAgentAccount + + +class TestDeleteDVPAgentAccount: + # Target API endpoint + base_url = "/settlement/dvp/agent/account/{account_address}" + + ########################################################################### + # Normal Case + ########################################################################### + + # + def test_normal_1(self, client, db): + test_account_address = "0x1234567890123456789012345678900000000000" + + # Prepare data + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = test_account_address + dvp_agent_account.keyfile = "test_keyfile_0" + dvp_agent_account.eoa_password = "test_password_0" + db.add(dvp_agent_account) + + db.commit() + + # Request target api + resp = client.delete(self.base_url.format(account_address=test_account_address)) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "account_address": "0x1234567890123456789012345678900000000000", + "is_deleted": True, + } + + dvp_agent_account_af = db.scalars( + select(DVPAgentAccount) + .where(DVPAgentAccount.account_address == test_account_address) + .limit(1) + ).first() + assert dvp_agent_account_af.is_deleted is True + + ########################################################################### + # Error Case + ########################################################################### + + # + def test_error_1(self, client, db): + test_account_address = "0x1234567890123456789012345678900000000000" + + # Request target api + resp = client.delete(self.base_url.format(account_address=test_account_address)) + + # Assertion + assert resp.status_code == 404 + assert resp.json() == { + "meta": {"code": 1, "title": "NotFound"}, + "detail": "account is not exists", + } diff --git a/tests/app/test_settlement_dvp_agent_account_{account_address}_eoa_password_POST.py b/tests/app/test_settlement_dvp_agent_account_{account_address}_eoa_password_POST.py new file mode 100644 index 00000000..2696c166 --- /dev/null +++ b/tests/app/test_settlement_dvp_agent_account_{account_address}_eoa_password_POST.py @@ -0,0 +1,204 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from sqlalchemy import select + +from app.model.db import DVPAgentAccount +from app.utils.e2ee_utils import E2EEUtils +from tests.account_config import config_eth_account + + +class TestChangeDVPAgentAccountPassword: + # Target API endpoint + base_url = "/settlement/dvp/agent/account/{account_address}/eoa_password" + + ########################################################################### + # Normal Case + ########################################################################### + + # + def test_normal_1(self, client, db): + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + user_keyfile_1 = user_1["keyfile_json"] + old_password = "password" + new_password = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 *+.\\()?[]^$-|!#%&\"',/:;<=>@_`{}~" + + # Prepare data + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = user_address_1 + dvp_agent_account.keyfile = user_keyfile_1 + dvp_agent_account.eoa_password = E2EEUtils.encrypt(old_password) + db.add(dvp_agent_account) + + db.commit() + + # Request target api + req_param = { + "old_eoa_password": E2EEUtils.encrypt(old_password), + "eoa_password": E2EEUtils.encrypt(new_password), + } + resp = client.post( + self.base_url.format(account_address=user_address_1), json=req_param + ) + + # Assertion + assert resp.status_code == 200 + + dvp_agent_account_af = db.scalars( + select(DVPAgentAccount) + .where(DVPAgentAccount.account_address == user_address_1) + .limit(1) + ).first() + assert E2EEUtils.decrypt(dvp_agent_account_af.eoa_password) == new_password + + ########################################################################### + # Error Case + ########################################################################### + + # + # Missing required fields + # -> RequestValidationError + def test_error_1(self, client, db): + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + + # Request target api + req_param = {} + resp = client.post( + self.base_url.format(account_address=user_address_1), json=req_param + ) + + # Assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "missing", + "loc": ["body", "old_eoa_password"], + "msg": "Field required", + "input": {}, + }, + { + "type": "missing", + "loc": ["body", "eoa_password"], + "msg": "Field required", + "input": {}, + }, + ], + } + + # + # Password is not encrypted + # -> RequestValidationError + def test_error_2(self, client, db): + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + + # Request target api + req_param = { + "old_eoa_password": "raw_password", + "eoa_password": "raw_password", + } + resp = client.post( + self.base_url.format(account_address=user_address_1), json=req_param + ) + + # Assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "value_error", + "loc": ["body", "old_eoa_password"], + "msg": "Value error, old_eoa_password is not a Base64-encoded encrypted data", + "input": "raw_password", + "ctx": {"error": {}}, + }, + { + "type": "value_error", + "loc": ["body", "eoa_password"], + "msg": "Value error, eoa_password is not a Base64-encoded encrypted data", + "input": "raw_password", + "ctx": {"error": {}}, + }, + ], + } + + # + # Log account is not exists + # -> NotFound + def test_error_3(self, client, db): + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + old_password = "password" + new_password = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 *+.\\()?[]^$-|!#%&\"',/:;<=>@_`{}~" + + # Request target api + req_param = { + "old_eoa_password": E2EEUtils.encrypt(old_password), + "eoa_password": E2EEUtils.encrypt(new_password), + } + resp = client.post( + self.base_url.format(account_address=user_address_1), json=req_param + ) + + # Assertion + assert resp.status_code == 404 + assert resp.json() == { + "meta": {"code": 1, "title": "NotFound"}, + "detail": "account is not exists", + } + + # + # New password violates password policy + # -> InvalidParameterError + def test_error_4(self, client, db): + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + user_keyfile_1 = user_1["keyfile_json"] + old_password = "password" + new_password = "password🚀" + + # Prepare data + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = user_address_1 + dvp_agent_account.keyfile = user_keyfile_1 + dvp_agent_account.eoa_password = E2EEUtils.encrypt(old_password) + db.add(dvp_agent_account) + + db.commit() + + # Request target api + req_param = { + "old_eoa_password": E2EEUtils.encrypt(old_password), + "eoa_password": E2EEUtils.encrypt(new_password), + } + resp = client.post( + self.base_url.format(account_address=user_address_1), json=req_param + ) + + # Assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 1, "title": "InvalidParameterError"}, + "detail": "password must be 8 to 200 alphanumeric or symbolic character", + } diff --git a/tests/app/test_settlement_dvp_agent_accounts_GET.py b/tests/app/test_settlement_dvp_agent_accounts_GET.py new file mode 100644 index 00000000..58a9f5d3 --- /dev/null +++ b/tests/app/test_settlement_dvp_agent_accounts_GET.py @@ -0,0 +1,73 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from app.model.db import DVPAgentAccount + + +class TestListAllDVPAgentAccounts: + # Target API endpoint + test_url = "/settlement/dvp/agent/accounts" + + ########################################################################### + # Normal Case + ########################################################################### + + # + # Data does not exist + def test_normal_1(self, client, db): + # Request target api + resp = client.get(self.test_url) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == [] + + # + # Data exist + def test_normal_2(self, client, db): + # Prepare data + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = "0x1234567890123456789012345678900000000000" + dvp_agent_account.keyfile = "test_keyfile_0" + dvp_agent_account.eoa_password = "test_password_0" + db.add(dvp_agent_account) + + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = "0x1234567890123456789012345678900000000001" + dvp_agent_account.keyfile = "test_keyfile_1" + dvp_agent_account.eoa_password = "test_password_1" + db.add(dvp_agent_account) + + db.commit() + + # Request target api + resp = client.get(self.test_url) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == [ + { + "account_address": "0x1234567890123456789012345678900000000000", + "is_deleted": False, + }, + { + "account_address": "0x1234567890123456789012345678900000000001", + "is_deleted": False, + }, + ] diff --git a/tests/app/test_settlement_dvp_agent_accounts_POST.py b/tests/app/test_settlement_dvp_agent_accounts_POST.py new file mode 100644 index 00000000..5892c1a3 --- /dev/null +++ b/tests/app/test_settlement_dvp_agent_accounts_POST.py @@ -0,0 +1,170 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import base64 +from unittest import mock + +from sqlalchemy import select + +from app.model.db import DVPAgentAccount, TransactionLock +from app.utils.e2ee_utils import E2EEUtils +from config import EOA_PASSWORD_PATTERN_MSG + + +class TestCreateDVPAgentAccount: + # Target API endpoint + test_url = "/settlement/dvp/agent/accounts" + + valid_password = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 *+.\\()?[]^$-|!#%&\"',/:;<=>@_`{}~" + invalid_password = "password🚀" + + ########################################################################### + # Normal Case + ########################################################################### + + # + # Use Linux RNG + def test_normal_1(self, client, db, ibet_security_token_dvp_contract): + # Request target api + req_param = {"eoa_password": E2EEUtils.encrypt(self.valid_password)} + resp = client.post(self.test_url, json=req_param) + + # Assertion + dvp_agent_account = db.scalars(select(DVPAgentAccount).limit(1)).first() + assert dvp_agent_account is not None + + tx_lock = db.scalars( + select(TransactionLock) + .where(TransactionLock.tx_from == dvp_agent_account.account_address) + .limit(1) + ).first() + assert tx_lock is not None + + assert resp.status_code == 200 + assert resp.json() == { + "account_address": dvp_agent_account.account_address, + "is_deleted": dvp_agent_account.is_deleted, + } + + # + # Use AWS RNG + def test_normal_2(self, client, db, ibet_security_token_dvp_contract): + # Mock setting + class KMSClientMock: + def generate_random(self, NumberOfBytes): + assert NumberOfBytes == 32 + return {"Plaintext": b"12345678901234567890123456789012"} + + mock_boto3_client = mock.patch( + target="boto3.client", side_effect=[KMSClientMock()] + ) + + # Request target api + with mock.patch( + "app.routers.settlement.AWS_KMS_GENERATE_RANDOM_ENABLED", True + ), mock_boto3_client: + req_param = {"eoa_password": E2EEUtils.encrypt(self.valid_password)} + resp = client.post(self.test_url, json=req_param) + + # Assertion + dvp_agent_account = db.scalars(select(DVPAgentAccount).limit(1)).first() + assert dvp_agent_account is not None + + tx_lock = db.scalars( + select(TransactionLock) + .where(TransactionLock.tx_from == dvp_agent_account.account_address) + .limit(1) + ).first() + assert tx_lock is not None + + assert resp.status_code == 200 + assert resp.json() == { + "account_address": dvp_agent_account.account_address, + "is_deleted": dvp_agent_account.is_deleted, + } + + ########################################################################### + # Error Case + ########################################################################### + + # + # Parameter Error + # Missing required field + # -> RequestValidationError + def test_error_1(self, client, db, ibet_security_token_dvp_contract): + # Request target api + req_param = {} + resp = client.post(self.test_url, json=req_param) + + # Assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "missing", + "loc": ["body", "eoa_password"], + "msg": "Field required", + "input": {}, + } + ], + } + + # + # Parameter Error + # Not encrypted password + # -> RequestValidationError + def test_error_2(self, client, db, ibet_security_token_dvp_contract): + # Request target api + req_param = { + "eoa_password": base64.encodebytes( + "password".encode("utf-8") + ).decode(), # Not encrypted + } + resp = client.post(self.test_url, json=req_param) + + # Assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "value_error", + "loc": ["body", "eoa_password"], + "msg": "Value error, eoa_password is not a Base64-encoded encrypted data", + "input": "cGFzc3dvcmQ=\n", + "ctx": {"error": {}}, + } + ], + } + + # + # Password policy violation + # -> InvalidParameterError + def test_error_3(self, client, db, ibet_security_token_dvp_contract): + # Request target api + req_param = {"eoa_password": E2EEUtils.encrypt(self.invalid_password)} + resp = client.post(self.test_url, json=req_param) + + # Assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 1, "title": "InvalidParameterError"}, + "detail": EOA_PASSWORD_PATTERN_MSG, + } diff --git a/tests/app/test_settlement_dvp_{exchange_address}_deliveries_GET.py b/tests/app/test_settlement_dvp_{exchange_address}_deliveries_GET.py new file mode 100644 index 00000000..b41a05a0 --- /dev/null +++ b/tests/app/test_settlement_dvp_{exchange_address}_deliveries_GET.py @@ -0,0 +1,2548 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from datetime import UTC, datetime + +import pytz + +from app.model.db import DeliveryStatus, IDXDelivery + + +class TestListAllDVPDeliveries: + # target API endpoint + base_url = "/settlement/dvp/{exchange_address}/deliveries" + + ########################################################################### + # Normal Case + ########################################################################### + + # Normal_1 + # 0 record + def test_normal_1(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": { + "count": 0, + "offset": None, + "limit": None, + "total": 0, + }, + "deliveries": [], + } + + # Normal_2 + # Multi record + def test_normal_2(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 6, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 1, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 3, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_CONFIRMED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 4, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": "2023-12-31T15:00:03+00:00", + "finish_transaction_hash": "tx_hash_4", + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_FINISHED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 5, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": "2023-12-31T15:00:04+00:00", + "abort_transaction_hash": "tx_hash_5", + "confirmed": True, + "valid": False, + "status": DeliveryStatus.DELIVERY_ABORTED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 6, + "token_address": token_address_2, + "buyer_address": buyer_address, + "seller_address": seller_address_2, + "amount": 1, + "agent_address": agent_address_2, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + ], + } + + # Normal_3_1 + # Search filter: token_address + def test_normal_3_1(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"token_address": token_address_1}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 5, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 1, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 3, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_CONFIRMED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 4, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": "2023-12-31T15:00:03+00:00", + "finish_transaction_hash": "tx_hash_4", + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_FINISHED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 5, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": "2023-12-31T15:00:04+00:00", + "abort_transaction_hash": "tx_hash_5", + "confirmed": True, + "valid": False, + "status": DeliveryStatus.DELIVERY_ABORTED, + }, + ], + } + + # Normal_3_2 + # Search filter: seller_address + def test_normal_3_2(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 2, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"seller_address": seller_address_1}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 5, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 1, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 3, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_CONFIRMED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 4, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": "2023-12-31T15:00:03+00:00", + "finish_transaction_hash": "tx_hash_4", + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_FINISHED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 5, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": "2023-12-31T15:00:04+00:00", + "abort_transaction_hash": "tx_hash_5", + "confirmed": True, + "valid": False, + "status": DeliveryStatus.DELIVERY_ABORTED, + }, + ], + } + + # Normal_3_3 + # Search filter: agent_address + def test_normal_3_3(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 2, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"agent_address": agent_address_1}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 5, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 1, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 3, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_CONFIRMED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 4, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": "2023-12-31T15:00:03+00:00", + "finish_transaction_hash": "tx_hash_4", + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_FINISHED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 5, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": "2023-12-31T15:00:04+00:00", + "abort_transaction_hash": "tx_hash_5", + "confirmed": True, + "valid": False, + "status": DeliveryStatus.DELIVERY_ABORTED, + }, + ], + } + + # Normal_3_4 + # Search filter: valid + def test_normal_3_4(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 2, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"valid": False}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 5, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": "2023-12-31T15:00:04+00:00", + "abort_transaction_hash": "tx_hash_5", + "confirmed": True, + "valid": False, + "status": DeliveryStatus.DELIVERY_ABORTED, + }, + ], + } + + # Normal_3_5 + # Search filter: status + def test_normal_3_5(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 2, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"status": DeliveryStatus.DELIVERY_FINISHED.value}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 4, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": "2023-12-31T15:00:03+00:00", + "finish_transaction_hash": "tx_hash_4", + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_FINISHED, + }, + ], + } + + # Normal_3_6_1 + # Search filter: create_blocktimestamp_from + def test_normal_3_6_1(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"create_blocktimestamp_from": str(datetime(2024, 1, 1, 9, 0, 1))}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 1, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:01+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + ], + } + + # Normal_3_6_2 + # Search filter: create_blocktimestamp_to + def test_normal_3_6_2(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 2, 0, 0, 5) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"create_blocktimestamp_to": str(datetime(2024, 1, 1, 9, 0, 1))}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:01+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 1, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + ], + } + + # Normal_3_6_3 + # Search filter: create_blocktimestamp_from & create_blocktimestamp_to + def test_normal_3_6_3(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 2, 0, 0, 5) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={ + "create_blocktimestamp_from": str(datetime(2024, 1, 1, 9, 0, 1)), + "create_blocktimestamp_to": str(datetime(2024, 1, 1, 9, 0, 1)), + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:01+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + ], + } + + # Normal_4 + # Sort + def test_normal_4(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 2, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"sort_order": 0}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 6, "limit": None, "offset": None, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 1, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:01+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 3, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:02+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_CONFIRMED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 4, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:03+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:03+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": "2023-12-31T15:00:03+00:00", + "finish_transaction_hash": "tx_hash_4", + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_FINISHED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 5, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:04+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:04+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": "2023-12-31T15:00:04+00:00", + "abort_transaction_hash": "tx_hash_5", + "confirmed": True, + "valid": False, + "status": DeliveryStatus.DELIVERY_ABORTED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 6, + "token_address": token_address_2, + "buyer_address": buyer_address, + "seller_address": seller_address_2, + "amount": 1, + "agent_address": agent_address_2, + "data": "", + "create_blocktimestamp": "2024-01-01T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + }, + ], + } + + # Normal_5 + # Pagination + def test_normal_5(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + token_address_2 = "0x1234567890123456789012345678900000000020" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + seller_address_2 = "0x1234567890123456789012345678900000000200" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + agent_address_2 = "0x1234567890123456789012345678900000002000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Canceled) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 2 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.cancel_blocktimestamp = datetime(2024, 1, 1, 0, 0, 1, tzinfo=UTC) + _idx_delivery.cancel_transaction_hash = "tx_hash_2" + _idx_delivery.confirmed = False + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_CANCELED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Confirmed) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 3 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CONFIRMED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Finished) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 4 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = datetime(2024, 1, 1, 0, 0, 3, tzinfo=UTC) + _idx_delivery.finish_transaction_hash = "tx_hash_4" + _idx_delivery.confirmed = True + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_FINISHED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Aborted) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 5 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirm_blocktimestamp = datetime(2024, 1, 1, 0, 0, 2, tzinfo=UTC) + _idx_delivery.confirm_transaction_hash = "tx_hash_3" + _idx_delivery.finish_blocktimestamp = None + _idx_delivery.finish_transaction_hash = None + _idx_delivery.abort_blocktimestamp = datetime(2024, 1, 1, 0, 0, 4, tzinfo=UTC) + _idx_delivery.abort_transaction_hash = "tx_hash_5" + _idx_delivery.confirmed = True + _idx_delivery.valid = False + _idx_delivery.status = DeliveryStatus.DELIVERY_ABORTED.value + db.add(_idx_delivery) + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 6 + _idx_delivery.token_address = token_address_2 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_2 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_2 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 2, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + db.commit() + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={"offset": 2, "limit": 2}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 6, "limit": 2, "offset": 2, "total": 6}, + "deliveries": [ + { + "exchange_address": exchange_address, + "delivery_id": 2, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": "2023-12-31T15:00:01+00:00", + "cancel_transaction_hash": "tx_hash_2", + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": False, + "status": DeliveryStatus.DELIVERY_CANCELED, + }, + { + "exchange_address": exchange_address, + "delivery_id": 3, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": "2023-12-31T15:00:02+00:00", + "confirm_transaction_hash": "tx_hash_3", + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": True, + "valid": True, + "status": DeliveryStatus.DELIVERY_CONFIRMED, + }, + ], + } + + # ########################################################################### + # # Error Case + # ########################################################################### + + # Error_1 + # RequestValidationError + # query(invalid value) + def test_error_1(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + + # request target api + resp = client.get( + self.base_url.format(exchange_address=exchange_address), + params={ + "valid": "invalid_value", + "status": "invalid_value", + "crate_blocktimestamp_from": "invalid_value", + "crate_blocktimestamp_to": "invalid_value", + "sort_item": "test", + "offset": "test", + "limit": "test", + }, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "bool_parsing", + "loc": ["query", "valid"], + "msg": "Input should be a valid boolean, unable to interpret input", + "input": "invalid_value", + }, + { + "type": "enum", + "loc": ["query", "status"], + "msg": "Input should be 0, 1, 2, 3 or 4", + "input": "invalid_value", + "ctx": {"expected": "0, 1, 2, 3 or 4"}, + }, + { + "type": "int_parsing", + "loc": ["query", "offset"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "test", + }, + { + "type": "int_parsing", + "loc": ["query", "limit"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "test", + }, + ], + } diff --git a/tests/app/test_settlement_dvp_{exchange_address}_deliveries_POST.py b/tests/app/test_settlement_dvp_{exchange_address}_deliveries_POST.py new file mode 100644 index 00000000..d925621c --- /dev/null +++ b/tests/app/test_settlement_dvp_{exchange_address}_deliveries_POST.py @@ -0,0 +1,734 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import asyncio +import hashlib +import json + +from eth_keyfile import decode_keyfile_json + +from app.model.blockchain import IbetStraightBondContract +from app.model.blockchain.tx_params.ibet_straight_bond import ( + UpdateParams as IbetStraightBondUpdateParams, +) +from app.model.db import Account, AuthToken, Token, TokenType, TokenVersion +from app.utils.contract_utils import ContractUtils +from app.utils.e2ee_utils import E2EEUtils +from config import CHAIN_ID, TX_GAS_LIMIT +from tests.account_config import config_eth_account + + +async def deploy_bond_token_contract( + address, + private_key, + personal_info_contract_address, + tradable_exchange_contract_address=None, + transfer_approval_required=None, +): + arguments = [ + "token.name", + "token.symbol", + 100, + 20, + "JPY", + "token.redemption_date", + 30, + "JPY", + "token.return_date", + "token.return_amount", + "token.purpose", + ] + bond_contrat = IbetStraightBondContract() + token_address, _, _ = await bond_contrat.create(arguments, address, private_key) + await bond_contrat.update( + data=IbetStraightBondUpdateParams( + transferable=True, + personal_info_contract_address=personal_info_contract_address, + tradable_exchange_contract_address=tradable_exchange_contract_address, + transfer_approval_required=transfer_approval_required, + ), + tx_from=address, + private_key=private_key, + ) + + return ContractUtils.get_contract("IbetStraightBond", token_address) + + +class TestCreateDVPDeliveriesPOST: + # target API endpoint + base_url = "/settlement/dvp/{exchange_address}/deliveries" + + ########################################################################### + # Normal Case + ########################################################################### + + # + # Authorization by eoa-password + def test_normal_1( + self, ibet_security_token_dvp_contract, personal_info_contract, client, db + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token_contract_1 = asyncio.run( + deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + ) + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = token_contract_1.address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # request target API + req_param = { + "token_address": token_contract_1.address, + "buyer_address": user_address_1, + "amount": 10, + "agent_address": agent_address, + "data": json.dumps({}), + } + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() is None + + # + # Authorization by auth-token + def test_normal_2( + self, ibet_security_token_dvp_contract, personal_info_contract, client, db + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token_contract_1 = asyncio.run( + deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + ) + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = token_contract_1.address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + auth_token = AuthToken() + auth_token.issuer_address = issuer_address + auth_token.auth_token = hashlib.sha256("test_auth_token".encode()).hexdigest() + auth_token.valid_duration = 0 + db.add(auth_token) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # request target API + req_param = { + "token_address": token_contract_1.address, + "buyer_address": user_address_1, + "amount": 10, + "agent_address": agent_address, + "data": json.dumps({}), + } + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "auth-token": "test_auth_token", + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() is None + + ########################################################################### + # Error Case + ########################################################################### + + # + # RequestValidationError: buyer_address, agent_address + def test_error_1(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = "0x0000000000000000000000000000000000000000" + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # request target API + req_param = {"buyer_address": "0x0", "agent_address": "0x0", "amount": 10} + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "input": { + "agent_address": "0x0", + "amount": 10, + "buyer_address": "0x0", + }, + "loc": ["body", "token_address"], + "msg": "Field required", + "type": "missing", + }, + { + "ctx": {"error": {}}, + "input": "0x0", + "loc": ["body", "buyer_address"], + "msg": "Value error, invalid ethereum address", + "type": "value_error", + }, + { + "ctx": {"error": {}}, + "input": "0x0", + "loc": ["body", "agent_address"], + "msg": "Value error, invalid ethereum address", + "type": "value_error", + }, + { + "input": { + "agent_address": "0x0", + "amount": 10, + "buyer_address": "0x0", + }, + "loc": ["body", "data"], + "msg": "Field required", + "type": "missing", + }, + ], + } + + # + # RequestValidationError: amount(min) + def test_error_2(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = "0x0000000000000000000000000000000000000000" + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # request target API + req_param = { + "token_address": "0x0000000000000000000000000000000000000000", + "buyer_address": "0x0000000000000000000000000000000000000000", + "amount": 0, + "agent_address": "0x0000000000000000000000000000000000000000", + "data": json.dumps({}), + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "ctx": {"ge": 1}, + "input": 0, + "loc": ["body", "amount"], + "msg": "Input should be greater than or equal to 1", + "type": "greater_than_equal", + } + ], + } + + # + # RequestValidationError: amount(max) + def test_error_3(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # request target API + req_param = { + "token_address": "0x0000000000000000000000000000000000000000", + "buyer_address": "0x0000000000000000000000000000000000000000", + "amount": 1_000_000_000_001, + "agent_address": "0x0000000000000000000000000000000000000000", + "data": json.dumps({}), + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "ctx": {"le": 1000000000000}, + "input": 1000000000001, + "loc": ["body", "amount"], + "msg": "Input should be less than or equal to 1000000000000", + "type": "less_than_equal", + } + ], + } + + # + # RequestValidationError: headers and body required + def test_error_4(self, client, db, ibet_security_token_dvp_contract): + # request target API + resp = client.post( + self.base_url.format( + exchange_address="0x0000000000000000000000000000000000000000" + ) + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "input": None, + "loc": ["header", "issuer-address"], + "msg": "Field required", + "type": "missing", + }, + { + "input": None, + "loc": ["body"], + "msg": "Field required", + "type": "missing", + }, + ], + } + + # + # RequestValidationError: issuer-address + def test_error_5(self, client, db, ibet_security_token_dvp_contract): + test_account = config_eth_account("user1") + issuer_address = test_account["address"] + + # request target API + req_param = { + "token_address": "0x0000000000000000000000000000000000000000", + "buyer_address": "0x0000000000000000000000000000000000000000", + "amount": 1, + "agent_address": "0x0000000000000000000000000000000000000000", + "data": json.dumps({}), + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={"issuer-address": "issuer-address"}, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "input": "issuer-address", + "loc": ["header", "issuer-address"], + "msg": "issuer-address is not a valid address", + "type": "value_error", + } + ], + } + + # + # RequestValidationError: eoa-password(not decrypt) + def test_error_6(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + db.commit() + + # request target API + req_param = { + "token_address": "0x0000000000000000000000000000000000000000", + "buyer_address": "0x0000000000000000000000000000000000000000", + "amount": 1, + "agent_address": "0x0000000000000000000000000000000000000000", + "data": json.dumps({}), + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={"issuer-address": issuer_address, "eoa-password": "password"}, + ) + + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "input": "password", + "loc": ["header", "eoa-password"], + "msg": "eoa-password is not a Base64-encoded encrypted data", + "type": "value_error", + } + ], + } + + # + # issuer does not exist + def test_error_7(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = "0x0000000000000000000000000000000000000000" + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # request target API + req_param = { + "token_address": "0x0000000000000000000000000000000000000000", + "buyer_address": "0x0000000000000000000000000000000000000000", + "amount": 1, + "agent_address": "0x0000000000000000000000000000000000000000", + "data": json.dumps({}), + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 401 + assert resp.json() == { + "meta": {"code": 1, "title": "AuthorizationError"}, + "detail": "issuer does not exist, or password mismatch", + } + + # + # password mismatch + def test_error_8(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + db.commit() + + # request target API + req_param = { + "token_address": "0x0000000000000000000000000000000000000000", + "buyer_address": "0x0000000000000000000000000000000000000000", + "amount": 1, + "agent_address": "0x0000000000000000000000000000000000000000", + "data": json.dumps({}), + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password_test"), + }, + ) + + assert resp.status_code == 401 + assert resp.json() == { + "meta": {"code": 1, "title": "AuthorizationError"}, + "detail": "issuer does not exist, or password mismatch", + } + + # + # Send Transaction Error + def test_error_9( + self, client, db, ibet_security_token_dvp_contract, personal_info_contract + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token_contract_1 = asyncio.run( + deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + ) + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = token_contract_1.address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # request target API + req_param = { + "token_address": token_contract_1.address, + "buyer_address": user_address_1, + "amount": 50, + "agent_address": agent_address, + "data": json.dumps({}), + } + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 2, "title": "SendTransactionError"}, + "detail": "failed to create delivery", + } diff --git a/tests/app/test_settlement_dvp_{exchange_address}_delivery_{delivery_id}_GET.py b/tests/app/test_settlement_dvp_{exchange_address}_delivery_{delivery_id}_GET.py new file mode 100644 index 00000000..d3583edc --- /dev/null +++ b/tests/app/test_settlement_dvp_{exchange_address}_delivery_{delivery_id}_GET.py @@ -0,0 +1,119 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from datetime import UTC, datetime + +from app.model.db import DeliveryStatus, IDXDelivery +from tests.account_config import config_eth_account + + +class TestAppRoutersShareTokensTokenAddressRedeemBatchBatchIdGET: + # target API endpoint + base_url = "/settlement/dvp/{exchange_address}/delivery/{delivery_id}" + + account_list = [ + {"address": config_eth_account("user1")["address"], "amount": 1}, + {"address": config_eth_account("user2")["address"], "amount": 2}, + ] + + ########################################################################### + # Normal Case + ########################################################################### + + # Normal_1 + def test_normal_1_1(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + token_address_1 = "0x1234567890123456789012345678900000000010" + + seller_address_1 = "0x1234567890123456789012345678900000000100" + + buyer_address = "0x1234567890123456789012345678911111111111" + + agent_address_1 = "0x1234567890123456789012345678900000001000" + + # prepare data: IDXDelivery(Created) + _idx_delivery = IDXDelivery() + _idx_delivery.exchange_address = exchange_address + _idx_delivery.delivery_id = 1 + _idx_delivery.token_address = token_address_1 + _idx_delivery.buyer_address = buyer_address + _idx_delivery.seller_address = seller_address_1 + _idx_delivery.amount = 1 + _idx_delivery.agent_address = agent_address_1 + _idx_delivery.data = "" + _idx_delivery.create_blocktimestamp = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + _idx_delivery.create_transaction_hash = "tx_hash_1" + _idx_delivery.confirmed = False + _idx_delivery.valid = True + _idx_delivery.status = DeliveryStatus.DELIVERY_CREATED.value + db.add(_idx_delivery) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(exchange_address=exchange_address, delivery_id=1), + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "exchange_address": exchange_address, + "delivery_id": 1, + "token_address": token_address_1, + "buyer_address": buyer_address, + "seller_address": seller_address_1, + "amount": 1, + "agent_address": agent_address_1, + "data": "", + "create_blocktimestamp": "2023-12-31T15:00:00+00:00", + "create_transaction_hash": "tx_hash_1", + "cancel_blocktimestamp": None, + "cancel_transaction_hash": None, + "confirm_blocktimestamp": None, + "confirm_transaction_hash": None, + "finish_blocktimestamp": None, + "finish_transaction_hash": None, + "abort_blocktimestamp": None, + "abort_transaction_hash": None, + "confirmed": False, + "valid": True, + "status": DeliveryStatus.DELIVERY_CREATED, + } + + ######################################################################### + # Error Case + ########################################################################### + + # Error_1 + # NotFound + def test_error_1(self, client, db): + exchange_address = "0x1234567890123456789012345678900000000000" + + # request target API + resp = client.get( + self.base_url.format(exchange_address=exchange_address, delivery_id=1), + ) + + # assertion + assert resp.status_code == 404 + assert resp.json() == { + "meta": {"code": 1, "title": "NotFound"}, + "detail": "delivery not found", + } diff --git a/tests/app/test_settlement_dvp_{exchange_address}_delivery_{delivery_id}_POST.py b/tests/app/test_settlement_dvp_{exchange_address}_delivery_{delivery_id}_POST.py new file mode 100644 index 00000000..d77574bb --- /dev/null +++ b/tests/app/test_settlement_dvp_{exchange_address}_delivery_{delivery_id}_POST.py @@ -0,0 +1,950 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import asyncio +import hashlib + +import pytest +from eth_keyfile import decode_keyfile_json + +from app.model.blockchain import IbetStraightBondContract +from app.model.blockchain.tx_params.ibet_straight_bond import ( + UpdateParams as IbetStraightBondUpdateParams, +) +from app.model.db import ( + Account, + AuthToken, + DVPAgentAccount, + Token, + TokenType, + TokenVersion, +) +from app.utils.contract_utils import ContractUtils +from app.utils.e2ee_utils import E2EEUtils +from config import CHAIN_ID, TX_GAS_LIMIT +from tests.account_config import config_eth_account + + +async def deploy_bond_token_contract( + address, + private_key, + personal_info_contract_address, + tradable_exchange_contract_address=None, + transfer_approval_required=None, +): + arguments = [ + "token.name", + "token.symbol", + 100, + 20, + "JPY", + "token.redemption_date", + 30, + "JPY", + "token.return_date", + "token.return_amount", + "token.purpose", + ] + bond_contrat = IbetStraightBondContract() + token_address, _, _ = await bond_contrat.create(arguments, address, private_key) + await bond_contrat.update( + data=IbetStraightBondUpdateParams( + transferable=True, + personal_info_contract_address=personal_info_contract_address, + tradable_exchange_contract_address=tradable_exchange_contract_address, + transfer_approval_required=transfer_approval_required, + ), + tx_from=address, + private_key=private_key, + ) + + return ContractUtils.get_contract("IbetStraightBond", token_address) + + +class TestUpdateDVPDeliveryPOST: + # target API endpoint + base_url = "/settlement/dvp/{exchange_address}/delivery/{delivery_id}" + + ########################################################################### + # Normal Case + ########################################################################### + + # + # Cancel Delivery + # Authorization by eoa-password + def test_normal_1_1( + self, ibet_security_token_dvp_contract, personal_info_contract, client, db + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = agent_address + dvp_agent_account.keyfile = user_3["keyfile_json"] + dvp_agent_account.eoa_password = E2EEUtils.encrypt("password") + db.add(dvp_agent_account) + + token_contract_1 = asyncio.run( + deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + ) + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = token_contract_1.address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_contract_1.address, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # request target API + req_param = {"operation_type": "Cancel"} + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, + delivery_id=1, + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() is None + + # + # Cancel Delivery + # Authorization by auth-token + def test_normal_1_2( + self, ibet_security_token_dvp_contract, personal_info_contract, client, db + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = agent_address + dvp_agent_account.keyfile = user_3["keyfile_json"] + dvp_agent_account.eoa_password = E2EEUtils.encrypt("password") + db.add(dvp_agent_account) + + token_contract_1 = asyncio.run( + deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + ) + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = token_contract_1.address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + auth_token = AuthToken() + auth_token.issuer_address = issuer_address + auth_token.auth_token = hashlib.sha256("test_auth_token".encode()).hexdigest() + auth_token.valid_duration = 0 + db.add(auth_token) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_contract_1.address, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # request target API + req_param = {"operation_type": "Cancel"} + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, + delivery_id=1, + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "auth-token": "test_auth_token", + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() is None + + # + # Finish Delivery + def test_normal_2( + self, ibet_security_token_dvp_contract, personal_info_contract, client, db + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = agent_address + dvp_agent_account.keyfile = user_3["keyfile_json"] + dvp_agent_account.eoa_password = E2EEUtils.encrypt("password") + db.add(dvp_agent_account) + + token_contract_1 = asyncio.run( + deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + ) + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = token_contract_1.address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_contract_1.address, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # ConfirmDelivery + tx = ibet_security_token_dvp_contract.functions.confirmDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": user_address_1, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, user_private_key_1) + + # request target API + req_param = { + "operation_type": "Finish", + "account_address": agent_address, + "eoa_password": E2EEUtils.encrypt("password"), + } + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, + delivery_id=1, + ), + json=req_param, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() is None + + # + # Finish Delivery + def test_normal_3( + self, ibet_security_token_dvp_contract, personal_info_contract, client, db + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = agent_address + dvp_agent_account.keyfile = user_3["keyfile_json"] + dvp_agent_account.eoa_password = E2EEUtils.encrypt("password") + db.add(dvp_agent_account) + + token_contract_1 = asyncio.run( + deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + ) + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = token_contract_1.address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_contract_1.address, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # ConfirmDelivery + tx = ibet_security_token_dvp_contract.functions.confirmDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": user_address_1, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, user_private_key_1) + + # request target API + req_param = { + "operation_type": "Finish", + "account_address": agent_address, + "eoa_password": E2EEUtils.encrypt("password"), + } + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, + delivery_id=1, + ), + json=req_param, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() is None + + ########################################################################### + # Error Case + ########################################################################### + + # + # RequestValidationError: operation_type + def test_error_1(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # request target API + req_param = {"operation_type": "invalid_value"} + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, delivery_id=1 + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "ctx": {"expected": "'Cancel'"}, + "input": "invalid_value", + "loc": ["body", "CancelDVPDeliveryRequest", "operation_type"], + "msg": "Input should be 'Cancel'", + "type": "literal_error", + }, + { + "ctx": {"expected": "'Finish'"}, + "input": "invalid_value", + "loc": ["body", "FinishDVPDeliveryRequest", "operation_type"], + "msg": "Input should be 'Finish'", + "type": "literal_error", + }, + { + "input": {"operation_type": "invalid_value"}, + "loc": ["body", "FinishDVPDeliveryRequest", "account_address"], + "msg": "Field required", + "type": "missing", + }, + { + "input": {"operation_type": "invalid_value"}, + "loc": ["body", "FinishDVPDeliveryRequest", "eoa_password"], + "msg": "Field required", + "type": "missing", + }, + { + "ctx": {"expected": "'Abort'"}, + "input": "invalid_value", + "loc": ["body", "AbortDVPDeliveryRequest", "operation_type"], + "msg": "Input should be 'Abort'", + "type": "literal_error", + }, + { + "input": {"operation_type": "invalid_value"}, + "loc": ["body", "AbortDVPDeliveryRequest", "account_address"], + "msg": "Field required", + "type": "missing", + }, + { + "input": {"operation_type": "invalid_value"}, + "loc": ["body", "AbortDVPDeliveryRequest", "eoa_password"], + "msg": "Field required", + "type": "missing", + }, + ], + } + + # + # RequestValidationError: headers and body required + def test_error_2(self, client, db, ibet_security_token_dvp_contract): + # request target API + resp = client.post( + self.base_url.format( + exchange_address="0x0000000000000000000000000000000000000000", + delivery_id=1, + ) + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "input": None, + "loc": ["body"], + "msg": "Field required", + "type": "missing", + } + ], + } + + # + # RequestValidationError: issuer-address + def test_error_3(self, client, db, ibet_security_token_dvp_contract): + test_account = config_eth_account("user1") + issuer_address = test_account["address"] + + # request target API + req_param = { + "operation_type": "Cancel", + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, delivery_id=1 + ), + json=req_param, + headers={"issuer-address": "issuer-address"}, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "input": "issuer-address", + "loc": ["header", "issuer-address"], + "msg": "issuer-address is not a valid address", + "type": "value_error", + } + ], + } + + # + # RequestValidationError: eoa-password(not decrypt) + def test_error_4(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + db.commit() + + # request target API + req_param = { + "operation_type": "Cancel", + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, delivery_id=1 + ), + json=req_param, + headers={"issuer-address": issuer_address, "eoa-password": "password"}, + ) + + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "input": "password", + "loc": ["header", "eoa-password"], + "msg": "eoa-password is not a Base64-encoded encrypted data", + "type": "value_error", + } + ], + } + + # + # issuer does not exist + def test_error_5(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = "0x0000000000000000000000000000000000000000" + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # request target API + req_param = { + "operation_type": "Cancel", + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, delivery_id=1 + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 401 + assert resp.json() == { + "meta": {"code": 1, "title": "AuthorizationError"}, + "detail": "issuer does not exist, or password mismatch", + } + + # + # password mismatch + def test_error_6(self, client, db, ibet_security_token_dvp_contract): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + db.commit() + + # request target API + req_param = { + "operation_type": "Cancel", + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, delivery_id=1 + ), + json=req_param, + headers={ + "issuer-address": issuer_address, + "eoa-password": E2EEUtils.encrypt("password_test"), + }, + ) + + assert resp.status_code == 401 + assert resp.json() == { + "meta": {"code": 1, "title": "AuthorizationError"}, + "detail": "issuer does not exist, or password mismatch", + } + + # + # DVP agent account not found + @pytest.mark.parametrize( + "operation_type", + ["Finish", "Abort"], + ) + def test_error_7( + self, operation_type, client, db, ibet_security_token_dvp_contract + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + db.commit() + + # request target API + req_param = { + "operation_type": operation_type, + "account_address": "0x0000000000000000000000000000000000000000", + "eoa_password": "password", + } + + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, delivery_id=1 + ), + json=req_param, + ) + + assert resp.status_code == 404, resp.json() + assert resp.json() == { + "meta": {"code": 1, "title": "NotFound"}, + "detail": "agent account is not exists", + } + + # + # Send Transaction Error + @pytest.mark.parametrize( + "operation_type", + ["Finish", "Abort"], + ) + def test_error_8( + self, + operation_type, + client, + db, + ibet_security_token_dvp_contract, + personal_info_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + _keyfile = user_1["keyfile_json"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # prepare data + account = Account() + account.issuer_address = issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + dvp_agent_account = DVPAgentAccount() + dvp_agent_account.account_address = agent_address + dvp_agent_account.keyfile = user_3["keyfile_json"] + dvp_agent_account.eoa_password = E2EEUtils.encrypt("password") + db.add(dvp_agent_account) + + token_contract_1 = asyncio.run( + deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + ) + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = issuer_address + token.token_address = token_contract_1.address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_contract_1.address, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # ConfirmDelivery + tx = ibet_security_token_dvp_contract.functions.confirmDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": user_address_1, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, user_private_key_1) + + # request target API + req_param = { + "operation_type": operation_type, + "account_address": agent_address, + "eoa_password": E2EEUtils.encrypt("password"), + } + resp = client.post( + self.base_url.format( + exchange_address=ibet_security_token_dvp_contract.address, + delivery_id=2, + ), + json=req_param, + ) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 2, "title": "SendTransactionError"}, + "detail": f"failed to {operation_type.lower()} delivery", + } diff --git a/tests/test_app_routers_share_bulk_transfer_GET.py b/tests/app/test_share_bulk_transfer_GET.py similarity index 97% rename from tests/test_app_routers_share_bulk_transfer_GET.py rename to tests/app/test_share_bulk_transfer_GET.py index 57c342a8..37ed19fe 100644 --- a/tests/test_app_routers_share_bulk_transfer_GET.py +++ b/tests/app/test_share_bulk_transfer_GET.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime +from datetime import UTC, datetime import pytest import pytz @@ -70,7 +70,7 @@ def test_normal_1(self, client, db): db.add(account) # prepare data : BulkTransferUpload - utc_now = datetime.utcnow() + utc_now = datetime.now(UTC).replace(tzinfo=None) for i in range(0, 3): bulk_transfer_upload = BulkTransferUpload() bulk_transfer_upload.issuer_address = self.upload_issuer_list[i]["address"] @@ -110,7 +110,7 @@ def test_normal_1(self, client, db): @pytest.mark.freeze_time("2021-05-20 12:34:56") def test_normal_2(self, client, db): # prepare data : BulkTransferUpload - utc_now = datetime.utcnow() + utc_now = datetime.now(UTC).replace(tzinfo=None) for i in range(0, 3): bulk_transfer_upload = BulkTransferUpload() bulk_transfer_upload.issuer_address = self.upload_issuer_list[i]["address"] diff --git a/tests/test_app_routers_share_bulk_transfer_POST.py b/tests/app/test_share_bulk_transfer_POST.py similarity index 98% rename from tests/test_app_routers_share_bulk_transfer_POST.py rename to tests/app/test_share_bulk_transfer_POST.py index 4de51217..2710b566 100644 --- a/tests/test_app_routers_share_bulk_transfer_POST.py +++ b/tests/app/test_share_bulk_transfer_POST.py @@ -84,7 +84,7 @@ def test_normal_1(self, client, db): _token.issuer_address = self.admin_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -174,7 +174,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.admin_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -257,7 +257,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.from_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -697,7 +697,7 @@ def test_error_10(self, client, db): _token.token_address = self.req_tokens[0] _token.abi = "" _token.token_status = 0 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -747,7 +747,7 @@ def test_error_11_1(self, client, db): _token.issuer_address = self.from_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -805,7 +805,7 @@ def test_error_11_2(self, client, db): _token.issuer_address = self.from_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -863,7 +863,7 @@ def test_error_11_3(self, client, db): _token.issuer_address = self.admin_address _token.token_address = _t _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_share_bulk_transfer_{upload_id}_GET.py b/tests/app/test_share_bulk_transfer_{upload_id}_GET.py similarity index 100% rename from tests/test_app_routers_share_bulk_transfer_{upload_id}_GET.py rename to tests/app/test_share_bulk_transfer_{upload_id}_GET.py diff --git a/tests/test_app_routers_share_lock_events_GET.py b/tests/app/test_share_lock_events_GET.py similarity index 97% rename from tests/test_app_routers_share_lock_events_GET.py rename to tests/app/test_share_lock_events_GET.py index 16ca2ca2..8c8c391e 100644 --- a/tests/test_app_routers_share_lock_events_GET.py +++ b/tests/app/test_share_lock_events_GET.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime +from datetime import UTC, datetime from unittest import mock from unittest.mock import ANY, AsyncMock @@ -56,7 +56,7 @@ def setup_data(self, db: Session, token_status: int = 1): _token.tx_hash = "" _token.abi = "" _token.token_status = token_status - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token @@ -67,7 +67,7 @@ def setup_data(self, db: Session, token_status: int = 1): _token.tx_hash = "" _token.abi = "" _token.token_status = token_status - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Lock events @@ -80,7 +80,7 @@ def setup_data(self, db: Session, token_status: int = 1): _lock.account_address = self.account_address_1 _lock.value = 1 _lock.data = {"message": "locked_1"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _lock = IDXLock() @@ -92,7 +92,7 @@ def setup_data(self, db: Session, token_status: int = 1): _lock.account_address = self.account_address_2 _lock.value = 1 _lock.data = {"message": "locked_2"} - _lock.block_timestamp = datetime.utcnow() + _lock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_lock) _unlock = IDXUnlock() @@ -105,7 +105,7 @@ def setup_data(self, db: Session, token_status: int = 1): _unlock.recipient_address = self.other_account_address_1 _unlock.value = 1 _unlock.data = {"message": "unlocked_1"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) _unlock = IDXUnlock() @@ -118,7 +118,7 @@ def setup_data(self, db: Session, token_status: int = 1): _unlock.recipient_address = self.other_account_address_2 _unlock.value = 1 _unlock.data = {"message": "unlocked_2"} - _unlock.block_timestamp = datetime.utcnow() + _unlock.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_unlock) db.commit() @@ -207,7 +207,7 @@ def test_normal_1(self, client, db): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_share_tokens_GET.py b/tests/app/test_share_tokens_GET.py similarity index 93% rename from tests/test_app_routers_share_tokens_GET.py rename to tests/app/test_share_tokens_GET.py index f9c98616..afe6ba70 100644 --- a/tests/test_app_routers_share_tokens_GET.py +++ b/tests/app/test_share_tokens_GET.py @@ -57,7 +57,7 @@ def test_normal_2(self, mock_get, client, db): token.issuer_address = issuer_address_1 token.token_address = "token_address_test1" token.abi = "abi_test1" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -91,6 +91,7 @@ def test_normal_2(self, mock_get, client, db): mock_token.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token.require_personal_info_registered = True mock_token.principal_value = 1000 mock_token.transfer_approval_required = False mock_token.is_canceled = False @@ -120,11 +121,12 @@ def test_normal_2(self, mock_get, client, db): "transfer_approval_required": False, "is_offering": True, "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "is_canceled": False, "issue_datetime": _issue_datetime, "token_status": 1, "memo": "memo_test1", - "contract_version": TokenVersion.V_22_12, + "contract_version": TokenVersion.V_24_06, } ] @@ -146,7 +148,7 @@ def test_normal_3(self, mock_get, client, db): token_1.issuer_address = issuer_address_1 token_1.token_address = "token_address_test1" token_1.abi = "abi_test1" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -179,6 +181,7 @@ def test_normal_3(self, mock_get, client, db): mock_token_1.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token_1.require_personal_info_registered = True mock_token_1.principal_value = 1000 mock_token_1.transfer_approval_required = False mock_token_1.is_canceled = False @@ -192,7 +195,7 @@ def test_normal_3(self, mock_get, client, db): token_2.token_address = "token_address_test2" token_2.abi = "abi_test2" token_2.token_status = 0 - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -225,6 +228,7 @@ def test_normal_3(self, mock_get, client, db): mock_token_2.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token_2.require_personal_info_registered = False mock_token_2.principal_value = 1000 mock_token_2.transfer_approval_required = False mock_token_2.is_canceled = False @@ -255,11 +259,12 @@ def test_normal_3(self, mock_get, client, db): "transfer_approval_required": False, "is_offering": True, "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "is_canceled": False, "issue_datetime": _issue_datetime_1, "token_status": 1, "memo": "memo_test1", - "contract_version": TokenVersion.V_22_12, + "contract_version": TokenVersion.V_24_06, }, { "issuer_address": issuer_address_2, @@ -281,11 +286,12 @@ def test_normal_3(self, mock_get, client, db): "transfer_approval_required": False, "is_offering": True, "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": False, "is_canceled": False, "issue_datetime": _issue_datetime_2, "token_status": 0, "memo": "memo_test2", - "contract_version": TokenVersion.V_22_12, + "contract_version": TokenVersion.V_24_06, }, ] @@ -304,7 +310,7 @@ def test_normal_4(self, client, db): token.issuer_address = "issuer_address_test1" token.token_address = "token_address_test1" token.abi = "abi_test1" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) resp = client.get(self.apiurl, headers={"issuer-address": issuer_address_1}) @@ -327,7 +333,7 @@ def test_normal_5(self, mock_get, client, db): token_1.issuer_address = issuer_address_1 token_1.token_address = "token_address_test1" token_1.abi = "abi_test1" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() _issue_datetime = ( @@ -359,6 +365,7 @@ def test_normal_5(self, mock_get, client, db): mock_token.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token.require_personal_info_registered = True mock_token.principal_value = 1000 mock_token.transfer_approval_required = False mock_token.is_canceled = False @@ -372,7 +379,7 @@ def test_normal_5(self, mock_get, client, db): token_2.issuer_address = issuer_address_2 token_2.token_address = "token_address_test1" token_2.abi = "abi_test1" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) resp = client.get(self.apiurl, headers={"issuer-address": issuer_address_1}) @@ -398,11 +405,12 @@ def test_normal_5(self, mock_get, client, db): "transfer_approval_required": False, "is_offering": True, "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "is_canceled": False, "issue_datetime": _issue_datetime, "token_status": 1, "memo": "memo_test1", - "contract_version": TokenVersion.V_22_12, + "contract_version": TokenVersion.V_24_06, } ] @@ -424,7 +432,7 @@ def test_normal_6(self, mock_get, client, db): token_1.issuer_address = issuer_address_1 token_1.token_address = "token_address_test1" token_1.abi = "abi_test1" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -457,6 +465,7 @@ def test_normal_6(self, mock_get, client, db): mock_token_1.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token_1.require_personal_info_registered = True mock_token_1.principal_value = 1000 mock_token_1.transfer_approval_required = False mock_token_1.is_canceled = False @@ -470,7 +479,7 @@ def test_normal_6(self, mock_get, client, db): token_2.token_address = "token_address_test2" token_2.abi = "abi_test2" token_2.token_status = 0 - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -503,6 +512,7 @@ def test_normal_6(self, mock_get, client, db): mock_token_2.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token_2.require_personal_info_registered = True mock_token_2.principal_value = 1000 mock_token_2.transfer_approval_required = False mock_token_2.is_canceled = False @@ -517,7 +527,7 @@ def test_normal_6(self, mock_get, client, db): token_3.issuer_address = issuer_address_2 token_3.token_address = "token_address_test1" token_3.abi = "abi_test1" - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) resp = client.get(self.apiurl, headers={"issuer-address": issuer_address_1}) @@ -543,11 +553,12 @@ def test_normal_6(self, mock_get, client, db): "transfer_approval_required": False, "is_offering": True, "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "is_canceled": False, "issue_datetime": _issue_datetime_1, "token_status": 1, "memo": "memo_test1", - "contract_version": TokenVersion.V_22_12, + "contract_version": TokenVersion.V_24_06, }, { "issuer_address": issuer_address_1, @@ -569,11 +580,12 @@ def test_normal_6(self, mock_get, client, db): "transfer_approval_required": False, "is_offering": True, "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "is_canceled": False, "issue_datetime": _issue_datetime_2, "token_status": 0, "memo": "memo_test2", - "contract_version": TokenVersion.V_22_12, + "contract_version": TokenVersion.V_24_06, }, ] diff --git a/tests/test_app_routers_share_tokens_POST.py b/tests/app/test_share_tokens_POST.py similarity index 97% rename from tests/test_app_routers_share_tokens_POST.py rename to tests/app/test_share_tokens_POST.py index 311b34cc..dfde08a5 100644 --- a/tests/test_app_routers_share_tokens_POST.py +++ b/tests/app/test_share_tokens_POST.py @@ -20,14 +20,14 @@ import hashlib import random import string -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest import mock from unittest.mock import ANY, patch import pytest from sqlalchemy import select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.exceptions import SendTransactionError @@ -49,7 +49,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) class TestAppRoutersShareTokensPOST: @@ -94,9 +94,7 @@ async def test_normal_1_1(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) @@ -168,7 +166,7 @@ async def test_normal_1_1(self, client, db): assert token_1.token_address == "contract_address_test1" assert token_1.abi == "abi_test1" assert token_1.token_status == 1 - assert token_1.version == TokenVersion.V_22_12 + assert token_1.version == TokenVersion.V_24_06 position = db.scalars(select(IDXPosition).limit(1)).first() assert position.token_address == "contract_address_test1" @@ -233,9 +231,7 @@ async def test_normal_1_2(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) @@ -292,7 +288,7 @@ async def test_normal_1_2(self, client, db): assert token_1.token_address == "contract_address_test1" assert token_1.abi == "abi_test1" assert token_1.token_status == 1 - assert token_1.version == TokenVersion.V_22_12 + assert token_1.version == TokenVersion.V_24_06 position = db.scalars(select(IDXPosition).limit(1)).first() assert position.token_address == "contract_address_test1" @@ -356,9 +352,7 @@ async def test_normal_2(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) @@ -377,6 +371,7 @@ async def test_normal_2(self, client, db): "cancellation_date": "20221231", "tradable_exchange_contract_address": "0x0000000000000000000000000000000000000001", # update "personal_info_contract_address": "0x0000000000000000000000000000000000000002", # update + "require_personal_info_registered": False, # update "transferable": False, # update "status": False, # update "is_offering": True, # update @@ -432,7 +427,7 @@ async def test_normal_2(self, client, db): assert token_1.token_address == "contract_address_test1" assert token_1.abi == "abi_test1" assert token_1.token_status == 0 - assert token_1.version == TokenVersion.V_22_12 + assert token_1.version == TokenVersion.V_24_06 position = db.scalars(select(IDXPosition).limit(1)).first() assert position is None @@ -489,9 +484,7 @@ async def test_normal_3(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) @@ -563,7 +556,7 @@ async def test_normal_3(self, client, db): assert token_1.token_address == "contract_address_test1" assert token_1.abi == "abi_test1" assert token_1.token_status == 1 - assert token_1.version == TokenVersion.V_22_12 + assert token_1.version == TokenVersion.V_24_06 position = db.scalars(select(IDXPosition).limit(1)).first() assert position.token_address == "contract_address_test1" @@ -624,9 +617,7 @@ def test_normal_4_1(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) @@ -706,9 +697,7 @@ def test_normal_4_2(self, client, db): target="app.utils.contract_utils.AsyncContractUtils.get_block_by_transaction_hash", return_value={ "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), }, ) diff --git a/tests/test_app_routers_share_tokens_{token_address}_GET.py b/tests/app/test_share_tokens_{token_address}_GET.py similarity index 94% rename from tests/test_app_routers_share_tokens_{token_address}_GET.py rename to tests/app/test_share_tokens_{token_address}_GET.py index 1efea0b3..7920cc0b 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_GET.py +++ b/tests/app/test_share_tokens_{token_address}_GET.py @@ -46,7 +46,7 @@ def test_normal_1(self, mock_get, client, db): token.issuer_address = "issuer_address_test1" token.token_address = "token_address_test1" token.abi = "abi_test1" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -80,6 +80,7 @@ def test_normal_1(self, mock_get, client, db): mock_token.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token.require_personal_info_registered = True mock_token.principal_value = 1000 mock_token.transfer_approval_required = False mock_token.is_canceled = False @@ -109,11 +110,12 @@ def test_normal_1(self, mock_get, client, db): "transfer_approval_required": False, "is_offering": True, "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "is_canceled": False, "issue_datetime": _issue_time, "token_status": 1, "memo": "memo_test1", - "contract_version": TokenVersion.V_22_12, + "contract_version": TokenVersion.V_24_06, } assert resp.status_code == 200 @@ -130,7 +132,7 @@ def test_normal_2(self, mock_get, client, db): token.issuer_address = "issuer_address_test1" token.token_address = "token_address_test1" token.abi = "abi_test1" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -164,6 +166,7 @@ def test_normal_2(self, mock_get, client, db): mock_token.personal_info_contract_address = ( "0x1234567890aBcDFE1234567890abcDFE12345679" ) + mock_token.require_personal_info_registered = True mock_token.principal_value = 1000 mock_token.transfer_approval_required = False mock_token.is_canceled = False @@ -193,11 +196,12 @@ def test_normal_2(self, mock_get, client, db): "transfer_approval_required": False, "is_offering": True, "personal_info_contract_address": "0x1234567890aBcDFE1234567890abcDFE12345679", + "require_personal_info_registered": True, "is_canceled": False, "issue_datetime": _issue_time, "token_status": 1, "memo": "memo_test1", - "contract_version": TokenVersion.V_22_12, + "contract_version": TokenVersion.V_24_06, } assert resp.status_code == 200 @@ -229,7 +233,7 @@ def test_error_2(self, client, db): token.token_address = "token_address_test1" token.abi = "abi_test1" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_POST.py b/tests/app/test_share_tokens_{token_address}_POST.py similarity index 94% rename from tests/test_app_routers_share_tokens_{token_address}_POST.py rename to tests/app/test_share_tokens_{token_address}_POST.py index 2df79157..b3d2a646 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_POST.py +++ b/tests/app/test_share_tokens_{token_address}_POST.py @@ -25,7 +25,7 @@ from eth_keyfile import decode_keyfile_json from sqlalchemy import select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.exceptions import SendTransactionError @@ -44,7 +44,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) async def deploy_share_token_contract( @@ -108,7 +108,7 @@ async def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -121,6 +121,7 @@ async def test_normal_1(self, client, db): "dividend_payment_date": "20211231", "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "transferable": False, "status": False, "is_offering": False, @@ -168,6 +169,7 @@ async def test_normal_1(self, client, db): "memo": "", "name": "token.name", "personal_info_contract_address": "0x0000000000000000000000000000000000000000", + "require_personal_info_registered": True, "principal_value": 30, "privacy_policy": "", "status": True, @@ -185,6 +187,7 @@ async def test_normal_1(self, client, db): "dividend_payment_date": "20211231", "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "transferable": False, "status": False, "is_offering": False, @@ -229,7 +232,7 @@ async def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -297,7 +300,7 @@ async def test_normal_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -310,6 +313,7 @@ async def test_normal_3(self, client, db): "dividend_payment_date": "20211231", "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "transferable": False, "status": False, "is_offering": False, @@ -358,6 +362,7 @@ async def test_normal_3(self, client, db): "memo": "", "name": "token.name", "personal_info_contract_address": "0x0000000000000000000000000000000000000000", + "require_personal_info_registered": True, "principal_value": 30, "privacy_policy": "", "status": True, @@ -375,6 +380,7 @@ async def test_normal_3(self, client, db): "dividend_payment_date": "20211231", "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "transferable": False, "status": False, "is_offering": False, @@ -419,7 +425,7 @@ async def test_normal_4_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -469,6 +475,7 @@ async def test_normal_4_1(self, client, db): "memo": "", "name": "token.name", "personal_info_contract_address": "0x0000000000000000000000000000000000000000", + "require_personal_info_registered": True, "principal_value": 30, "privacy_policy": "", "status": True, @@ -519,7 +526,7 @@ async def test_normal_4_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -569,6 +576,7 @@ async def test_normal_4_2(self, client, db): "memo": "", "name": "token.name", "personal_info_contract_address": "0x0000000000000000000000000000000000000000", + "require_personal_info_registered": True, "principal_value": 30, "privacy_policy": "", "status": True, @@ -1026,7 +1034,7 @@ def test_error_12(self, IbetShareContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -1152,7 +1160,7 @@ def test_error_15(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -1200,7 +1208,7 @@ def test_error_16(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -1222,3 +1230,50 @@ def test_error_16(self, client, db): "meta": {"code": 2, "title": "SendTransactionError"}, "detail": "failed to send transaction", } + + # + # OperationNotSupportedVersionError: v24.6 + def test_error_17(self, client, db): + test_account = config_eth_account("user1") + _issuer_address = test_account["address"] + _keyfile = test_account["keyfile_json"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + + # prepare data + account = Account() + account.issuer_address = _issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token = Token() + token.type = TokenType.IBET_SHARE.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.token_status = 1 + token.version = TokenVersion.V_22_12 + db.add(token) + + db.commit() + + # request target API + req_param = { + "require_personal_info_registered": False, + } + resp = client.post( + self.base_url.format(_token_address), + json=req_param, + headers={ + "issuer-address": _issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 6, "title": "OperationNotSupportedVersionError"}, + "detail": "the operation is not supported in 22_12", + } diff --git a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_GET.py b/tests/app/test_share_tokens_{token_address}_additional_issue_GET.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_additional_issue_GET.py rename to tests/app/test_share_tokens_{token_address}_additional_issue_GET.py index f1b13267..0047fbd8 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_GET.py +++ b/tests/app/test_share_tokens_{token_address}_additional_issue_GET.py @@ -74,7 +74,7 @@ def test_normal_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -110,7 +110,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -191,7 +191,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -278,7 +278,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -365,7 +365,7 @@ def test_error_2(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -425,7 +425,7 @@ def test_error_4_1(self, client, db): "detail": [ { "ctx": {"expected": "0 or 1"}, - "input": -1, + "input": "-1", "loc": ["query", "sort_order"], "msg": "Input should be 0 or 1", "type": "enum", @@ -449,7 +449,7 @@ def test_error_4_2(self, client, db): "detail": [ { "ctx": {"expected": "0 or 1"}, - "input": 2, + "input": "2", "loc": ["query", "sort_order"], "msg": "Input should be 0 or 1", "type": "enum", diff --git a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_POST.py b/tests/app/test_share_tokens_{token_address}_additional_issue_POST.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_additional_issue_POST.py rename to tests/app/test_share_tokens_{token_address}_additional_issue_POST.py index 495dfba6..988379ee 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_POST.py +++ b/tests/app/test_share_tokens_{token_address}_additional_issue_POST.py @@ -58,7 +58,7 @@ def test_normal_1(self, IbetShareContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -115,7 +115,7 @@ def test_normal_2(self, IbetShareContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -287,7 +287,7 @@ def test_error_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -330,7 +330,7 @@ def test_error_6(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -444,7 +444,7 @@ def test_error_9(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -492,7 +492,7 @@ def test_error_10(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_GET.py b/tests/app/test_share_tokens_{token_address}_additional_issue_batch_GET.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_GET.py rename to tests/app/test_share_tokens_{token_address}_additional_issue_batch_GET.py index 838867b9..0208037c 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_GET.py +++ b/tests/app/test_share_tokens_{token_address}_additional_issue_batch_GET.py @@ -52,7 +52,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -80,7 +80,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -129,7 +129,7 @@ def test_normal_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -246,7 +246,7 @@ def test_normal_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -350,7 +350,7 @@ def test_normal_3_3(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -460,7 +460,7 @@ def test_normal_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -562,7 +562,7 @@ def test_normal_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -685,7 +685,7 @@ def test_error_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_POST.py b/tests/app/test_share_tokens_{token_address}_additional_issue_batch_POST.py similarity index 97% rename from tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_POST.py rename to tests/app/test_share_tokens_{token_address}_additional_issue_batch_POST.py index acd9f681..edd7c9c0 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_POST.py +++ b/tests/app/test_share_tokens_{token_address}_additional_issue_batch_POST.py @@ -66,7 +66,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -131,7 +131,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -206,7 +206,7 @@ def test_error_1_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -258,7 +258,7 @@ def test_error_1_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -313,7 +313,7 @@ def test_error_1_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -368,7 +368,7 @@ def test_error_1_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -423,7 +423,7 @@ def test_error_1_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -472,7 +472,7 @@ def test_error_1_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -521,7 +521,7 @@ def test_error_1_6(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -568,7 +568,7 @@ def test_error_1_7_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -615,7 +615,7 @@ def test_error_1_7_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -703,7 +703,7 @@ def test_error_1_8_2(self, client, db): token.token_address = token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py b/tests/app/test_share_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py rename to tests/app/test_share_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py index 95d790d4..a6618700 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py +++ b/tests/app/test_share_tokens_{token_address}_additional_issue_batch_{batch_id}_GET.py @@ -71,7 +71,7 @@ def test_normal_1_1(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -158,7 +158,7 @@ def test_normal_1_2(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -245,7 +245,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -358,7 +358,7 @@ def test_error_1(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() diff --git a/tests/test_app_routers_share_tokens_{token_address}_history_GET.py b/tests/app/test_share_tokens_{token_address}_history_GET.py similarity index 96% rename from tests/test_app_routers_share_tokens_{token_address}_history_GET.py rename to tests/app/test_share_tokens_{token_address}_history_GET.py index 2bfd869b..ebc695ec 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_history_GET.py +++ b/tests/app/test_share_tokens_{token_address}_history_GET.py @@ -27,7 +27,7 @@ from starlette.testclient import TestClient from web3 import Web3 from web3.contract import Contract -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.model.blockchain import IbetShareContract @@ -45,7 +45,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) async def deploy_share_token_contract( @@ -87,6 +87,7 @@ async def deploy_share_token_contract( is_offering=True, # update tradable_exchange_contract_address=tradable_exchange_contract_address, # update personal_info_contract_address=personal_info_contract_address, # update + require_personal_info_registered=False, # update contact_information="contact info test", # update privacy_policy="privacy policy test", # update transfer_approval_required=transfer_approval_required, # update @@ -135,6 +136,10 @@ async def deploy_share_token_contract( token_create_param["personal_info_contract_address"] ).build_transaction(build_tx_param) ContractUtils.send_transaction(transaction=tx, private_key=private_key) + tx = contract.functions.setRequirePersonalInfoRegistered( + token_create_param["require_personal_info_registered"] + ).build_transaction(build_tx_param) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) tx = contract.functions.setContactInformation( token_create_param["contact_information"] ).build_transaction(build_tx_param) @@ -232,7 +237,7 @@ def test_normal_1(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -285,7 +290,7 @@ async def test_normal_2(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -388,7 +393,7 @@ async def test_normal_3_1(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -489,7 +494,7 @@ async def test_normal_3_2(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -577,7 +582,7 @@ async def test_normal_3_3(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _operation_log_1 = TokenUpdateOperationLog() @@ -617,7 +622,7 @@ async def test_normal_3_3(self, client, db, personal_info_contract): resp = client.get( self.base_url.format(_token_address), params={ - "created_from": str(datetime(2023, 5, 3, 8, 0, 0)), + "created_from": "2023-05-03 08:00:00", }, ) @@ -635,13 +640,13 @@ async def test_normal_3_3(self, client, db, personal_info_contract): "original_contents": {}, "modified_contents": {"memo": "20230504"}, "operation_category": TokenUpdateOperationCategory.UPDATE.value, - "created": ANY, + "created": "2023-05-04T09:00:00+09:00", }, { "original_contents": {}, "modified_contents": {"memo": "20230503"}, "operation_category": TokenUpdateOperationCategory.UPDATE.value, - "created": ANY, + "created": "2023-05-03T09:00:00+09:00", }, ], } @@ -681,7 +686,7 @@ async def test_normal_3_4(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) _operation_log_1 = TokenUpdateOperationLog() @@ -723,7 +728,7 @@ async def test_normal_3_4(self, client, db, personal_info_contract): resp = client.get( self.base_url.format(_token_address), params={ - "created_to": str(datetime(2023, 5, 2, 0, 0, 0)), + "created_to": "2023-05-02 00:00:00", }, ) @@ -741,7 +746,7 @@ async def test_normal_3_4(self, client, db, personal_info_contract): "original_contents": None, "modified_contents": create_param, "operation_category": TokenUpdateOperationCategory.ISSUE.value, - "created": ANY, + "created": "2023-05-01T09:00:00+09:00", }, ], } @@ -777,7 +782,7 @@ async def test_normal_4_1(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -884,7 +889,7 @@ async def test_normal_4_2(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -992,7 +997,7 @@ async def test_normal_5_1(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1080,7 +1085,7 @@ async def test_normal_5_2(self, client, db, personal_info_contract): _token.type = TokenType.IBET_SHARE.value _token.tx_hash = "" _token.abi = "" - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1137,39 +1142,37 @@ def test_error_1(self, client, db): "meta": {"code": 1, "title": "RequestValidationError"}, "detail": [ { - "ctx": {"expected": "'Issue' or 'Update'"}, - "input": "test", + "type": "enum", "loc": ["query", "operation_category"], "msg": "Input should be 'Issue' or 'Update'", - "type": "enum", + "input": "test", + "ctx": {"expected": "'Issue' or 'Update'"}, }, { - "ctx": {"expected": "'created' or 'operation_category'"}, - "input": "test", + "type": "enum", "loc": ["query", "sort_item"], "msg": "Input should be 'created' or 'operation_category'", - "type": "enum", + "input": "test", + "ctx": {"expected": "'created' or 'operation_category'"}, }, { - "input": "test", + "type": "enum", "loc": ["query", "sort_order"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", - "type": "int_parsing", + "msg": "Input should be 0 or 1", + "input": "test", + "ctx": {"expected": "0 or 1"}, }, { - "input": "test", - "loc": ["query", "offset"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", "type": "int_parsing", + "loc": ["query", "offset"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "test", }, { - "input": "test", - "loc": ["query", "limit"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", "type": "int_parsing", + "loc": ["query", "limit"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "test", }, ], } diff --git a/tests/test_app_routers_share_tokens_{token_address}_holders_GET.py b/tests/app/test_share_tokens_{token_address}_holders_GET.py similarity index 85% rename from tests/test_app_routers_share_tokens_{token_address}_holders_GET.py rename to tests/app/test_share_tokens_{token_address}_holders_GET.py index 760a74e6..88a167be 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_holders_GET.py +++ b/tests/app/test_share_tokens_{token_address}_holders_GET.py @@ -57,7 +57,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -95,7 +95,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: Position @@ -212,7 +212,7 @@ def test_normal_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -441,7 +441,7 @@ def test_normal_4_1_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -590,7 +590,7 @@ def test_normal_4_1_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -747,7 +747,7 @@ def test_normal_4_2_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -939,7 +939,7 @@ def test_normal_4_2_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1150,7 +1150,7 @@ def test_normal_4_2_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1361,7 +1361,7 @@ def test_normal_4_3_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1553,7 +1553,7 @@ def test_normal_4_3_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1764,7 +1764,7 @@ def test_normal_4_3_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -1975,7 +1975,7 @@ def test_normal_4_4_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2167,7 +2167,7 @@ def test_normal_4_4_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2367,6 +2367,7 @@ def test_normal_4_4_3(self, client, db): _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + _account_address_4 = "0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec" account = Account() account.issuer_address = _issuer_address @@ -2378,7 +2379,657 @@ def test_normal_4_4_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 + db.add(token) + + # prepare data: account_address_1 + idx_position_1 = IDXPosition() + idx_position_1.token_address = _token_address + idx_position_1.account_address = _account_address_1 + idx_position_1.balance = 10 + idx_position_1.exchange_balance = 11 + idx_position_1.exchange_commitment = 12 + idx_position_1.pending_transfer = 5 + idx_position_1.modified = datetime(2023, 10, 24, 0, 0, 0) + db.add(idx_position_1) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 10, 0) + db.add(idx_locked_position) + + idx_personal_info_1 = IDXPersonalInfo() + idx_personal_info_1.account_address = _account_address_1 + idx_personal_info_1.issuer_address = _issuer_address + idx_personal_info_1.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(idx_personal_info_1) + + # prepare data: account_address_2 + idx_position_2 = IDXPosition() + idx_position_2.token_address = _token_address + idx_position_2.account_address = _account_address_2 + idx_position_2.balance = 20 + idx_position_2.exchange_balance = 21 + idx_position_2.exchange_commitment = 22 + idx_position_2.pending_transfer = 10 + idx_position_2.modified = datetime(2023, 10, 24, 2, 0, 0) + db.add(idx_position_2) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 10, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 20, 0) + db.add(idx_locked_position) + + # prepare data: account_address_3 + idx_position_3 = IDXPosition() + idx_position_3.token_address = _token_address + idx_position_3.account_address = _account_address_3 + idx_position_3.balance = 99 + idx_position_3.exchange_balance = 99 + idx_position_3.exchange_commitment = 99 + idx_position_3.pending_transfer = 99 + idx_position_3.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_3) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 4, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 5, 0, 0) + db.add(idx_locked_position) + + # Other locked position + _locked_position = IDXLockedPosition() + _locked_position.token_address = "other_token_address" + _locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + _locked_position.account_address = _account_address_1 + _locked_position.value = 5 + _locked_position.modified = datetime(2023, 10, 25, 0, 2, 0) + db.add(_locked_position) + + idx_personal_info_3 = IDXPersonalInfo() + idx_personal_info_3.account_address = _account_address_3 + idx_personal_info_3.issuer_address = _issuer_address + idx_personal_info_3.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + # PersonalInfo is partially registered. + } + db.add(idx_personal_info_3) + + # prepare data: account_address_4 + idx_position_4 = IDXPosition() + idx_position_4.token_address = _token_address + idx_position_4.account_address = _account_address_4 + idx_position_4.balance = 99 + idx_position_4.exchange_balance = 99 + idx_position_4.exchange_commitment = 99 + idx_position_4.pending_transfer = 99 + idx_position_4.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_4) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(_token_address), + headers={"issuer-address": _issuer_address}, + params={"locked": 20, "locked_operator": 2}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "limit": None, "offset": None, "total": 4}, + "holders": [ + { + "account_address": _account_address_1, + "personal_information": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "balance": 10, + "exchange_balance": 11, + "exchange_commitment": 12, + "pending_transfer": 5, + "locked": 10, + "modified": "2023-10-24T01:10:00", + }, + { + "account_address": _account_address_2, + "personal_information": { + "key_manager": None, + "name": None, + "postal_code": None, + "address": None, + "email": None, + "birth": None, + "is_corporate": None, + "tax_category": None, + }, + "balance": 20, + "exchange_balance": 21, + "exchange_commitment": 22, + "pending_transfer": 10, + "locked": 20, + "modified": "2023-10-24T02:20:00", + }, + { + "account_address": _account_address_4, + "pending_transfer": 99, + "personal_information": { + "address": None, + "birth": None, + "email": None, + "is_corporate": None, + "key_manager": None, + "name": None, + "postal_code": None, + "tax_category": None, + }, + "balance": 99, + "exchange_balance": 99, + "exchange_commitment": 99, + "locked": 0, + "modified": "2023-10-24T03:00:00", + }, + ], + } + + # + # Search filter: balance + pending_transfer & "=" + def test_normal_4_5_1(self, client, db): + user = config_eth_account("user1") + _issuer_address = user["address"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" + _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" + _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + + account = Account() + account.issuer_address = _issuer_address + db.add(account) + + token = Token() + token.type = TokenType.IBET_SHARE.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + # prepare data: account_address_1 + idx_position_1 = IDXPosition() + idx_position_1.token_address = _token_address + idx_position_1.account_address = _account_address_1 + idx_position_1.balance = 10 + idx_position_1.exchange_balance = 11 + idx_position_1.exchange_commitment = 12 + idx_position_1.pending_transfer = 5 + idx_position_1.modified = datetime(2023, 10, 24, 0, 0, 0) + db.add(idx_position_1) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 10, 0) + db.add(idx_locked_position) + + idx_personal_info_1 = IDXPersonalInfo() + idx_personal_info_1.account_address = _account_address_1 + idx_personal_info_1.issuer_address = _issuer_address + idx_personal_info_1.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(idx_personal_info_1) + + # prepare data: account_address_2 + idx_position_2 = IDXPosition() + idx_position_2.token_address = _token_address + idx_position_2.account_address = _account_address_2 + idx_position_2.balance = 20 + idx_position_2.exchange_balance = 21 + idx_position_2.exchange_commitment = 22 + idx_position_2.pending_transfer = 10 + idx_position_2.modified = datetime(2023, 10, 24, 2, 0, 0) + db.add(idx_position_2) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 10, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 20, 0) + db.add(idx_locked_position) + + # prepare data: account_address_3 + idx_position_3 = IDXPosition() + idx_position_3.token_address = _token_address + idx_position_3.account_address = _account_address_3 + idx_position_3.balance = 99 + idx_position_3.exchange_balance = 99 + idx_position_3.exchange_commitment = 99 + idx_position_3.pending_transfer = 99 + idx_position_3.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_3) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 4, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 5, 0, 0) + db.add(idx_locked_position) + + # Other locked position + _locked_position = IDXLockedPosition() + _locked_position.token_address = "other_token_address" + _locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + _locked_position.account_address = _account_address_1 + _locked_position.value = 5 + _locked_position.modified = datetime(2023, 10, 25, 0, 2, 0) + db.add(_locked_position) + + idx_personal_info_3 = IDXPersonalInfo() + idx_personal_info_3.account_address = _account_address_3 + idx_personal_info_3.issuer_address = _issuer_address + idx_personal_info_3.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + # PersonalInfo is partially registered. + } + db.add(idx_personal_info_3) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(_token_address), + headers={"issuer-address": _issuer_address}, + params={ + "balance_and_pending_transfer": 30, + "balance_and_pending_transfer_operator": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "total": 3, "offset": None, "limit": None}, + "holders": [ + { + "account_address": _account_address_2, + "personal_information": { + "key_manager": None, + "name": None, + "postal_code": None, + "address": None, + "email": None, + "birth": None, + "is_corporate": None, + "tax_category": None, + }, + "balance": 20, + "exchange_balance": 21, + "exchange_commitment": 22, + "pending_transfer": 10, + "locked": 20, + "modified": "2023-10-24T02:20:00", + }, + ], + } + + # + # Search filter: balance + pending_transfer & ">=" + def test_normal_4_5_2(self, client, db): + user = config_eth_account("user1") + _issuer_address = user["address"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" + _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" + _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + + account = Account() + account.issuer_address = _issuer_address + db.add(account) + + token = Token() + token.type = TokenType.IBET_SHARE.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + # prepare data: account_address_1 + idx_position_1 = IDXPosition() + idx_position_1.token_address = _token_address + idx_position_1.account_address = _account_address_1 + idx_position_1.balance = 10 + idx_position_1.exchange_balance = 11 + idx_position_1.exchange_commitment = 12 + idx_position_1.pending_transfer = 5 + idx_position_1.modified = datetime(2023, 10, 24, 0, 0, 0) + db.add(idx_position_1) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 10, 0) + db.add(idx_locked_position) + + idx_personal_info_1 = IDXPersonalInfo() + idx_personal_info_1.account_address = _account_address_1 + idx_personal_info_1.issuer_address = _issuer_address + idx_personal_info_1.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(idx_personal_info_1) + + # prepare data: account_address_2 + idx_position_2 = IDXPosition() + idx_position_2.token_address = _token_address + idx_position_2.account_address = _account_address_2 + idx_position_2.balance = 20 + idx_position_2.exchange_balance = 21 + idx_position_2.exchange_commitment = 22 + idx_position_2.pending_transfer = 10 + idx_position_2.modified = datetime(2023, 10, 24, 2, 0, 0) + db.add(idx_position_2) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 10, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 20, 0) + db.add(idx_locked_position) + + # prepare data: account_address_3 + idx_position_3 = IDXPosition() + idx_position_3.token_address = _token_address + idx_position_3.account_address = _account_address_3 + idx_position_3.balance = 99 + idx_position_3.exchange_balance = 99 + idx_position_3.exchange_commitment = 99 + idx_position_3.pending_transfer = 99 + idx_position_3.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_3) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 4, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 5, 0, 0) + db.add(idx_locked_position) + + # Other locked position + _locked_position = IDXLockedPosition() + _locked_position.token_address = "other_token_address" + _locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + _locked_position.account_address = _account_address_1 + _locked_position.value = 5 + _locked_position.modified = datetime(2023, 10, 25, 0, 2, 0) + db.add(_locked_position) + + idx_personal_info_3 = IDXPersonalInfo() + idx_personal_info_3.account_address = _account_address_3 + idx_personal_info_3.issuer_address = _issuer_address + idx_personal_info_3.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + # PersonalInfo is partially registered. + } + db.add(idx_personal_info_3) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(_token_address), + headers={"issuer-address": _issuer_address}, + params={ + "balance_and_pending_transfer": 30, + "balance_and_pending_transfer_operator": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "total": 3, "offset": None, "limit": None}, + "holders": [ + { + "account_address": _account_address_2, + "personal_information": { + "key_manager": None, + "name": None, + "postal_code": None, + "address": None, + "email": None, + "birth": None, + "is_corporate": None, + "tax_category": None, + }, + "balance": 20, + "exchange_balance": 21, + "exchange_commitment": 22, + "pending_transfer": 10, + "locked": 20, + "modified": "2023-10-24T02:20:00", + }, + { + "account_address": _account_address_3, + "personal_information": { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": None, + "tax_category": None, + }, + "balance": 99, + "exchange_balance": 99, + "exchange_commitment": 99, + "pending_transfer": 99, + "locked": 30, + "modified": "2023-10-24T05:00:00", + }, + ], + } + + # + # Search filter: balance + pending_transfer & "<=" + def test_normal_4_5_3(self, client, db): + user = config_eth_account("user1") + _issuer_address = user["address"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" + _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" + _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + + account = Account() + account.issuer_address = _issuer_address + db.add(account) + + token = Token() + token.type = TokenType.IBET_SHARE.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2520,7 +3171,10 @@ def test_normal_4_4_3(self, client, db): resp = client.get( self.base_url.format(_token_address), headers={"issuer-address": _issuer_address}, - params={"locked": 20, "locked_operator": 2}, + params={ + "balance_and_pending_transfer": 30, + "balance_and_pending_transfer_operator": 2, + }, ) # assertion @@ -2569,9 +3223,9 @@ def test_normal_4_4_3(self, client, db): ], } - # + # # Search filter: holder_name - def test_normal_4_5(self, client, db): + def test_normal_4_6(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" @@ -2589,7 +3243,7 @@ def test_normal_4_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2761,9 +3415,9 @@ def test_normal_4_5(self, client, db): ], } - # + # # Search filter: key_manager - def test_normal_4_6(self, client, db): + def test_normal_4_7(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" @@ -2781,7 +3435,7 @@ def test_normal_4_6(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -2972,9 +3626,9 @@ def test_normal_4_6(self, client, db): ], } - # + # # Search filter: account_address - def test_normal_4_7(self, client, db): + def test_normal_4_8(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" @@ -2992,7 +3646,7 @@ def test_normal_4_7(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -3184,7 +3838,7 @@ def test_normal_5_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -3417,7 +4071,7 @@ def test_normal_5_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -3650,7 +4304,7 @@ def test_normal_5_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -3883,7 +4537,7 @@ def test_normal_5_4(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -4116,7 +4770,7 @@ def test_normal_5_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -4330,7 +4984,7 @@ def test_normal_5_5(self, client, db): } # - # Sort Item: holder_name + # Sort Item: balance + pending_transfer def test_normal_5_6(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] @@ -4338,6 +4992,239 @@ def test_normal_5_6(self, client, db): _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" + + account = Account() + account.issuer_address = _issuer_address + db.add(account) + + token = Token() + token.type = TokenType.IBET_SHARE.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_24_06 + db.add(token) + + # prepare data: account_address_1 + idx_position_1 = IDXPosition() + idx_position_1.token_address = _token_address + idx_position_1.account_address = _account_address_1 + idx_position_1.balance = 10 + idx_position_1.exchange_balance = 11 + idx_position_1.exchange_commitment = 12 + idx_position_1.pending_transfer = 5 + idx_position_1.created = datetime(2023, 10, 24, 0, 0, 0) + idx_position_1.modified = datetime(2023, 10, 24, 0, 0, 0) + db.add(idx_position_1) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_1 + idx_locked_position.value = 5 + idx_locked_position.modified = datetime(2023, 10, 24, 1, 10, 0) + db.add(idx_locked_position) + + idx_personal_info_1 = IDXPersonalInfo() + idx_personal_info_1.account_address = _account_address_1 + idx_personal_info_1.issuer_address = _issuer_address + idx_personal_info_1.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(idx_personal_info_1) + + # prepare data: account_address_2 + idx_position_2 = IDXPosition() + idx_position_2.token_address = _token_address + idx_position_2.account_address = _account_address_2 + idx_position_2.balance = 20 + idx_position_2.exchange_balance = 21 + idx_position_2.exchange_commitment = 22 + idx_position_2.pending_transfer = 10 + idx_position_2.created = datetime(2023, 10, 24, 2, 0, 0) + idx_position_2.modified = datetime(2023, 10, 24, 2, 0, 0) + db.add(idx_position_2) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 10, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_2 + idx_locked_position.value = 10 + idx_locked_position.modified = datetime(2023, 10, 24, 2, 20, 0) + db.add(idx_locked_position) + + # prepare data: account_address_3 + idx_position_3 = IDXPosition() + idx_position_3.token_address = _token_address + idx_position_3.account_address = _account_address_3 + idx_position_3.balance = 99 + idx_position_3.exchange_balance = 99 + idx_position_3.exchange_commitment = 99 + idx_position_3.pending_transfer = 99 + idx_position_3.created = datetime(2023, 10, 24, 3, 0, 0) + idx_position_3.modified = datetime(2023, 10, 24, 3, 0, 0) + db.add(idx_position_3) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000001" # lock address 1 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 4, 0, 0) + db.add(idx_locked_position) + + idx_locked_position = IDXLockedPosition() + idx_locked_position.token_address = _token_address + idx_locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + idx_locked_position.account_address = _account_address_3 + idx_locked_position.value = 15 + idx_locked_position.modified = datetime(2023, 10, 24, 5, 0, 0) + db.add(idx_locked_position) + + # Other locked position + _locked_position = IDXLockedPosition() + _locked_position.token_address = "other_token_address" + _locked_position.lock_address = ( + "0x1234567890123456789012345678900000000002" # lock address 2 + ) + _locked_position.account_address = _account_address_1 + _locked_position.value = 5 + _locked_position.modified = datetime(2023, 10, 25, 0, 2, 0) + db.add(_locked_position) + + idx_personal_info_3 = IDXPersonalInfo() + idx_personal_info_3.account_address = _account_address_3 + idx_personal_info_3.issuer_address = _issuer_address + idx_personal_info_3.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + # PersonalInfo is partially registered. + } + db.add(idx_personal_info_3) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(_token_address), + headers={"issuer-address": _issuer_address}, + params={"sort_order": 1, "sort_item": "balance_and_pending_transfer"}, + ) + + # assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "total": 3, "offset": None, "limit": None}, + "holders": [ + { + "account_address": _account_address_3, + "personal_information": { + "key_manager": "key_manager_test1", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": None, + "tax_category": None, + }, + "balance": 99, + "exchange_balance": 99, + "exchange_commitment": 99, + "pending_transfer": 99, + "locked": 30, + "modified": "2023-10-24T05:00:00", + }, + { + "account_address": _account_address_2, + "personal_information": { + "key_manager": None, + "name": None, + "postal_code": None, + "address": None, + "email": None, + "birth": None, + "is_corporate": None, + "tax_category": None, + }, + "balance": 20, + "exchange_balance": 21, + "exchange_commitment": 22, + "pending_transfer": 10, + "locked": 20, + "modified": "2023-10-24T02:20:00", + }, + { + "account_address": _account_address_1, + "personal_information": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "balance": 10, + "exchange_balance": 11, + "exchange_commitment": 12, + "pending_transfer": 5, + "locked": 10, + "modified": "2023-10-24T01:10:00", + }, + ], + } + + # + # Sort Item: holder_name + def test_normal_5_7(self, client, db): + user = config_eth_account("user1") + _issuer_address = user["address"] + _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" + _account_address_1 = "0xb75c7545b9230FEe99b7af370D38eBd3DAD929f7" + _account_address_2 = "0x3F198534Bbe3B2a197d3B317d41392F348EAC707" + _account_address_3 = "0x8277D905F37F8a9717F5718d0daC21495dFE74bf" _account_address_4 = "0x917eFFaC072dcda308e2337636f562D0A96F42eA" account = Account() @@ -4350,7 +5237,7 @@ def test_normal_5_6(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -4594,9 +5481,9 @@ def test_normal_5_6(self, client, db): ], } - # + # # Sort Item: key_manager - def test_normal_5_7(self, client, db): + def test_normal_5_8(self, client, db): user = config_eth_account("user1") _issuer_address = user["address"] _token_address = "0x82b1c9374aB625380bd498a3d9dF4033B8A0E3Bb" @@ -4615,7 +5502,7 @@ def test_normal_5_7(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -4879,7 +5766,7 @@ def test_normal_6_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -5090,7 +5977,7 @@ def test_normal_6_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: account_address_1 @@ -5331,7 +6218,7 @@ def test_error_4(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data diff --git a/tests/test_app_routers_share_tokens_{token_address}_holders_count_GET.py b/tests/app/test_share_tokens_{token_address}_holders_count_GET.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_holders_count_GET.py rename to tests/app/test_share_tokens_{token_address}_holders_count_GET.py index caf651be..c92a00b5 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_holders_count_GET.py +++ b/tests/app/test_share_tokens_{token_address}_holders_count_GET.py @@ -55,7 +55,7 @@ def test_normal_1_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -90,7 +90,7 @@ def test_normal_1_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -134,7 +134,7 @@ def test_normal_1_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -196,7 +196,7 @@ def test_normal_1_4(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -250,7 +250,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -417,7 +417,7 @@ def test_error_4(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_holders_{account_address}_GET.py b/tests/app/test_share_tokens_{token_address}_holders_{account_address}_GET.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_holders_{account_address}_GET.py rename to tests/app/test_share_tokens_{token_address}_holders_{account_address}_GET.py index 52c153f8..d65a4e42 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_holders_{account_address}_GET.py +++ b/tests/app/test_share_tokens_{token_address}_holders_{account_address}_GET.py @@ -60,7 +60,7 @@ def test_normal_1_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: Personal Info @@ -130,7 +130,7 @@ def test_normal_1_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: Position @@ -211,7 +211,7 @@ def test_normal_1_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # prepare data: Position @@ -311,7 +311,7 @@ def test_normal_2_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -373,7 +373,7 @@ def test_normal_2_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) idx_position_1 = IDXPosition() @@ -529,7 +529,7 @@ def test_error_4(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_personal_info_POST.py b/tests/app/test_share_tokens_{token_address}_personal_info_POST.py similarity index 99% rename from tests/test_app_routers_share_tokens_{token_address}_personal_info_POST.py rename to tests/app/test_share_tokens_{token_address}_personal_info_POST.py index 19350ac6..b4e55b75 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_personal_info_POST.py +++ b/tests/app/test_share_tokens_{token_address}_personal_info_POST.py @@ -59,7 +59,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -140,7 +140,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -227,7 +227,7 @@ def test_normal_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -680,7 +680,7 @@ def test_error_4(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -738,7 +738,7 @@ def test_error_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_GET.py b/tests/app/test_share_tokens_{token_address}_personal_info_batch_GET.py similarity index 97% rename from tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_GET.py rename to tests/app/test_share_tokens_{token_address}_personal_info_batch_GET.py index ffdb0222..2c21df4b 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_GET.py +++ b/tests/app/test_share_tokens_{token_address}_personal_info_batch_GET.py @@ -61,7 +61,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -94,7 +94,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload @@ -153,7 +153,7 @@ def test_normal_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload @@ -208,7 +208,7 @@ def test_normal_4(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload @@ -263,7 +263,7 @@ def test_normal_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload @@ -327,7 +327,7 @@ def test_error_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -370,7 +370,7 @@ def test_error_2(self, client, db): token.issuer_address = _issuer_address_2 token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload diff --git a/tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_POST.py b/tests/app/test_share_tokens_{token_address}_personal_info_batch_POST.py similarity index 99% rename from tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_POST.py rename to tests/app/test_share_tokens_{token_address}_personal_info_batch_POST.py index 6fbe7814..a1398fd8 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_POST.py +++ b/tests/app/test_share_tokens_{token_address}_personal_info_batch_POST.py @@ -70,7 +70,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -147,7 +147,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -668,7 +668,7 @@ def test_error_4_1(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -730,7 +730,7 @@ def test_error_4_2(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 1 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py b/tests/app/test_share_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py similarity index 99% rename from tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py rename to tests/app/test_share_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py index 17b55235..2c49b538 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py +++ b/tests/app/test_share_tokens_{token_address}_personal_info_batch_{batch_id}_GET.py @@ -94,7 +94,7 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) # Prepare data : BatchRegisterPersonalInfoUpload diff --git a/tests/test_app_routers_share_tokens_{token_address}_redeem_GET.py b/tests/app/test_share_tokens_{token_address}_redeem_GET.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_redeem_GET.py rename to tests/app/test_share_tokens_{token_address}_redeem_GET.py index 6fe6f33b..e85f8fb1 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_redeem_GET.py +++ b/tests/app/test_share_tokens_{token_address}_redeem_GET.py @@ -74,7 +74,7 @@ def test_normal_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -110,7 +110,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -191,7 +191,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -278,7 +278,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXIssueRedeem @@ -365,7 +365,7 @@ def test_error_2(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -425,7 +425,7 @@ def test_error_4_1(self, client, db): "detail": [ { "ctx": {"expected": "0 or 1"}, - "input": -1, + "input": "-1", "loc": ["query", "sort_order"], "msg": "Input should be 0 or 1", "type": "enum", @@ -449,7 +449,7 @@ def test_error_4_2(self, client, db): "detail": [ { "ctx": {"expected": "0 or 1"}, - "input": 2, + "input": "2", "loc": ["query", "sort_order"], "msg": "Input should be 0 or 1", "type": "enum", diff --git a/tests/test_app_routers_share_tokens_{token_address}_redeem_POST.py b/tests/app/test_share_tokens_{token_address}_redeem_POST.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_redeem_POST.py rename to tests/app/test_share_tokens_{token_address}_redeem_POST.py index 810b41d4..7edbd68c 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_redeem_POST.py +++ b/tests/app/test_share_tokens_{token_address}_redeem_POST.py @@ -58,7 +58,7 @@ def test_normal_1(self, IbetShareContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -113,7 +113,7 @@ def test_normal_2(self, IbetShareContract_mock, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -283,7 +283,7 @@ def test_error_5(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -326,7 +326,7 @@ def test_error_6(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -440,7 +440,7 @@ def test_error_9(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -488,7 +488,7 @@ def test_error_10(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_redeem_batch_GET.py b/tests/app/test_share_tokens_{token_address}_redeem_batch_GET.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_redeem_batch_GET.py rename to tests/app/test_share_tokens_{token_address}_redeem_batch_GET.py index 22806df0..ceed3d88 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_redeem_batch_GET.py +++ b/tests/app/test_share_tokens_{token_address}_redeem_batch_GET.py @@ -52,7 +52,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -80,7 +80,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -127,7 +127,7 @@ def test_normal_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -234,7 +234,7 @@ def test_normal_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -328,7 +328,7 @@ def test_normal_3_3(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -428,7 +428,7 @@ def test_normal_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) redeem_upload1 = BatchIssueRedeemUpload() @@ -520,7 +520,7 @@ def test_normal_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) additional_issue_upload1 = BatchIssueRedeemUpload() @@ -643,7 +643,7 @@ def test_error_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_redeem_batch_POST.py b/tests/app/test_share_tokens_{token_address}_redeem_batch_POST.py similarity index 97% rename from tests/test_app_routers_share_tokens_{token_address}_redeem_batch_POST.py rename to tests/app/test_share_tokens_{token_address}_redeem_batch_POST.py index c2a8a2ba..a7f56bb1 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_redeem_batch_POST.py +++ b/tests/app/test_share_tokens_{token_address}_redeem_batch_POST.py @@ -66,7 +66,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -131,7 +131,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -206,7 +206,7 @@ def test_error_1_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -258,7 +258,7 @@ def test_error_1_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -313,7 +313,7 @@ def test_error_1_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -368,7 +368,7 @@ def test_error_1_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -423,7 +423,7 @@ def test_error_1_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -472,7 +472,7 @@ def test_error_1_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -521,7 +521,7 @@ def test_error_1_6(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -568,7 +568,7 @@ def test_error_1_7_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -615,7 +615,7 @@ def test_error_1_7_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -703,7 +703,7 @@ def test_error_1_8_2(self, client, db): token.token_address = token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_share_tokens_{token_address}_redeem_batch_{batch_id}_GET.py b/tests/app/test_share_tokens_{token_address}_redeem_batch_{batch_id}_GET.py similarity index 98% rename from tests/test_app_routers_share_tokens_{token_address}_redeem_batch_{batch_id}_GET.py rename to tests/app/test_share_tokens_{token_address}_redeem_batch_{batch_id}_GET.py index 3a77f631..f31eeeb4 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_redeem_batch_{batch_id}_GET.py +++ b/tests/app/test_share_tokens_{token_address}_redeem_batch_{batch_id}_GET.py @@ -71,7 +71,7 @@ def test_normal_1_1(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -158,7 +158,7 @@ def test_normal_1_2(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -245,7 +245,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() @@ -358,7 +358,7 @@ def test_error_1(self, client, db): token.issuer_address = issuer_address token.token_address = test_token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) batch_upload = BatchIssueRedeemUpload() diff --git a/tests/test_app_routers_share_tokens_{token_address}_scheduled_events_GET.py b/tests/app/test_share_tokens_{token_address}_scheduled_events_GET.py similarity index 93% rename from tests/test_app_routers_share_tokens_{token_address}_scheduled_events_GET.py rename to tests/app/test_share_tokens_{token_address}_scheduled_events_GET.py index ac156b14..86b5b6a3 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_scheduled_events_GET.py +++ b/tests/app/test_share_tokens_{token_address}_scheduled_events_GET.py @@ -18,9 +18,9 @@ """ import uuid -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta -from pytz import timezone +import pytz from app.model.db import ScheduledEvents, ScheduledEventType, TokenType from config import TZ @@ -30,7 +30,7 @@ class TestAppRoutersShareTokensTokenAddressScheduledEventsGET: # target API endpoint base_url = "/share/tokens/{}/scheduled_events" - local_tz = timezone(TZ) + local_tz = pytz.timezone(TZ) ########################################################################### # Normal Case @@ -45,9 +45,9 @@ def test_normal_1(self, client, db): _token_address = "token_address_test" # prepare data - datetime_now_utc = datetime.utcnow() + datetime_now_utc = datetime.now(UTC).replace(tzinfo=None) datetime_now_str = ( - timezone("UTC") + pytz.timezone("UTC") .localize(datetime_now_utc) .astimezone(self.local_tz) .isoformat() @@ -117,9 +117,9 @@ def test_normal_2(self, client, db): # prepare data datetime_list = [] - datetime_utc = datetime.utcnow() + timedelta(hours=1) + datetime_utc = datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1) datetime_list.append(datetime_utc) - datetime_utc = datetime.utcnow() + datetime_utc = datetime.now(UTC).replace(tzinfo=None) datetime_list.append(datetime_utc) uuid_list = [str(uuid.uuid4()), str(uuid.uuid4())] @@ -167,14 +167,14 @@ def test_normal_2(self, client, db): "scheduled_event_id": uuid_list[0], "token_address": _token_address, "token_type": TokenType.IBET_SHARE.value, - "scheduled_datetime": timezone("UTC") + "scheduled_datetime": pytz.timezone("UTC") .localize(datetime_list[0]) .astimezone(self.local_tz) .isoformat(), "event_type": ScheduledEventType.UPDATE.value, "status": 0, "data": update_data, - "created": timezone("UTC") + "created": pytz.timezone("UTC") .localize(datetime_list[0]) .astimezone(self.local_tz) .isoformat(), @@ -183,14 +183,14 @@ def test_normal_2(self, client, db): "scheduled_event_id": uuid_list[1], "token_address": _token_address, "token_type": TokenType.IBET_SHARE.value, - "scheduled_datetime": timezone("UTC") + "scheduled_datetime": pytz.timezone("UTC") .localize(datetime_list[1]) .astimezone(self.local_tz) .isoformat(), "event_type": ScheduledEventType.UPDATE.value, "status": 0, "data": update_data, - "created": timezone("UTC") + "created": pytz.timezone("UTC") .localize(datetime_list[1]) .astimezone(self.local_tz) .isoformat(), diff --git a/tests/test_app_routers_share_tokens_{token_address}_scheduled_events_POST.py b/tests/app/test_share_tokens_{token_address}_scheduled_events_POST.py similarity index 87% rename from tests/test_app_routers_share_tokens_{token_address}_scheduled_events_POST.py rename to tests/app/test_share_tokens_{token_address}_scheduled_events_POST.py index c3e33e10..75594707 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_scheduled_events_POST.py +++ b/tests/app/test_share_tokens_{token_address}_scheduled_events_POST.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime, timezone +from datetime import UTC, datetime from pytz import timezone as tz from sqlalchemy import and_, select @@ -63,13 +63,13 @@ def test_normal_1(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() # test data - datetime_now_utc = datetime.now(timezone.utc) # utc + datetime_now_utc = datetime.now(UTC) # utc datetime_now_str = datetime_now_utc.isoformat() update_data = { "cancellation_date": "20221231", @@ -78,6 +78,7 @@ def test_normal_1(self, client, db): "dividend_payment_date": "20211231", "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "transferable": False, "status": False, "is_offering": False, @@ -146,7 +147,7 @@ def test_normal_2(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -161,6 +162,7 @@ def test_normal_2(self, client, db): "dividend_payment_date": "20211231", "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "transferable": False, "status": False, "is_offering": False, @@ -198,7 +200,7 @@ def test_normal_2(self, client, db): assert resp.json() == {"scheduled_event_id": _scheduled_event.event_id} assert _scheduled_event.token_type == TokenType.IBET_SHARE.value assert _scheduled_event.scheduled_datetime == datetime_now_jst.astimezone( - timezone.utc + UTC ).replace(tzinfo=None) assert _scheduled_event.event_type == ScheduledEventType.UPDATE.value assert _scheduled_event.status == 0 @@ -216,7 +218,7 @@ def test_error_1_1(self, client, db): _token_address = "token_address_test" # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -261,7 +263,7 @@ def test_error_1_2(self, client, db): _token_address = "token_address_test" # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {"is_canceled": False} @@ -302,7 +304,7 @@ def test_error_2(self, client, db): _token_address = "token_address_test" # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -348,13 +350,13 @@ def test_error_3(self, client, db): token.issuer_address = _issuer_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -397,7 +399,7 @@ def test_error_4(self, client, db): db.commit() # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -495,13 +497,13 @@ def test_error_6(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() # test data - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -526,3 +528,59 @@ def test_error_6(self, client, db): "meta": {"code": 1, "title": "InvalidParameterError"}, "detail": "this token is temporarily unavailable", } + + # + # OperationNotSupportedVersionError: v24.6 + def test_error_7(self, client, db): + test_account = config_eth_account("user1") + _issuer_address = test_account["address"] + _keyfile = test_account["keyfile_json"] + _token_address = "token_address_test" + + # prepare data + account = Account() + account.issuer_address = _issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token = Token() + token.type = TokenType.IBET_SHARE.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.token_status = 1 + token.version = TokenVersion.V_22_12 + db.add(token) + + db.commit() + + # test data + datetime_now_utc = datetime.now(UTC) + datetime_now_str = datetime_now_utc.isoformat() + update_data = { + "require_personal_info_registered": False, + } + + # request target API + req_param = { + "scheduled_datetime": datetime_now_str, + "event_type": "Update", + "data": update_data, + } + resp = client.post( + self.base_url.format(_token_address), + json=req_param, + headers={ + "issuer-address": _issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 6, "title": "OperationNotSupportedVersionError"}, + "detail": "the operation is not supported in 22_12", + } diff --git a/tests/test_app_routers_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py b/tests/app/test_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py similarity index 97% rename from tests/test_app_routers_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py rename to tests/app/test_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py index a331be58..f2bd9265 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py +++ b/tests/app/test_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_DELETE.py @@ -18,9 +18,9 @@ """ import uuid -from datetime import datetime +from datetime import UTC, datetime -from pytz import timezone +import pytz from sqlalchemy import select from app.model.db import Account, ScheduledEvents, ScheduledEventType, TokenType @@ -32,7 +32,7 @@ class TestAppRoutersShareTokensTokenAddressScheduledEventsScheduledEventIdDELETE: # target API endpoint base_url = "/share/tokens/{}/scheduled_events/{}" - local_tz = timezone(TZ) + local_tz = pytz.timezone(TZ) ########################################################################### # Normal Case @@ -52,9 +52,9 @@ def test_normal_1(self, client, db): account.eoa_password = E2EEUtils.encrypt("password") db.add(account) - datetime_now_utc = datetime.utcnow() + datetime_now_utc = datetime.now(UTC).replace(tzinfo=None) datetime_now_str = ( - timezone("UTC") + pytz.timezone("UTC") .localize(datetime_now_utc) .astimezone(self.local_tz) .isoformat() diff --git a/tests/test_app_routers_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py b/tests/app/test_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py similarity index 95% rename from tests/test_app_routers_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py rename to tests/app/test_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py index 12c9e1a3..ebfe10d2 100644 --- a/tests/test_app_routers_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py +++ b/tests/app/test_share_tokens_{token_address}_scheduled_events_{scheduled_event_id}_GET.py @@ -18,9 +18,9 @@ """ import uuid -from datetime import datetime +from datetime import UTC, datetime -from pytz import timezone +import pytz from app.model.db import ScheduledEvents, ScheduledEventType, TokenType from config import TZ @@ -30,7 +30,7 @@ class TestAppRoutersShareTokensTokenAddressScheduledEventsScheduledEventIdGET: # target API endpoint base_url = "/share/tokens/{}/scheduled_events/{}" - local_tz = timezone(TZ) + local_tz = pytz.timezone(TZ) ########################################################################### # Normal Case @@ -45,9 +45,9 @@ def test_normal_1(self, client, db): _token_address = "token_address_test" # prepare data - datetime_now_utc = datetime.utcnow() + datetime_now_utc = datetime.now(UTC).replace(tzinfo=None) datetime_now_str = ( - timezone("UTC") + pytz.timezone("UTC") .localize(datetime_now_utc) .astimezone(self.local_tz) .isoformat() @@ -113,9 +113,9 @@ def test_normal_2(self, client, db): _token_address = "token_address_test" # prepare data - datetime_now_utc = datetime.utcnow() + datetime_now_utc = datetime.now(UTC).replace(tzinfo=None) datetime_now_str = ( - timezone("UTC") + pytz.timezone("UTC") .localize(datetime_now_utc) .astimezone(self.local_tz) .isoformat() diff --git a/tests/test_app_routers_share_transfer_approvals_GET.py b/tests/app/test_share_transfer_approvals_GET.py similarity index 98% rename from tests/test_app_routers_share_transfer_approvals_GET.py rename to tests/app/test_share_transfer_approvals_GET.py index 266d8756..a3728b3f 100644 --- a/tests/test_app_routers_share_transfer_approvals_GET.py +++ b/tests/app/test_share_transfer_approvals_GET.py @@ -100,7 +100,7 @@ def test_normal_1(self, client, db): _token.token_address = self.test_token_address_1 _token.abi = {} _token.token_status = 2 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(bond) @@ -110,7 +110,7 @@ def test_normal_1(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_2 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(failed token) @@ -201,7 +201,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_1 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(ApplyFor(unapproved)) @@ -386,7 +386,7 @@ def test_normal_3_1(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_1 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -396,7 +396,7 @@ def test_normal_3_1(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_2 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-2) @@ -406,7 +406,7 @@ def test_normal_3_1(self, client, db): _token.issuer_address = self.test_issuer_address_2 _token.token_address = self.test_token_address_3 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(issuer-1 token-1) @@ -571,7 +571,7 @@ def test_normal_3_2(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_1 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -581,7 +581,7 @@ def test_normal_3_2(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_2 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-2) @@ -591,7 +591,7 @@ def test_normal_3_2(self, client, db): _token.issuer_address = self.test_issuer_address_2 _token.token_address = self.test_token_address_3 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(issuer-1 token-1) @@ -763,7 +763,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_1 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -773,7 +773,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_2 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -783,7 +783,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_3 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: Token(issuer-1) @@ -793,7 +793,7 @@ def test_normal_4(self, client, db): _token.issuer_address = self.test_issuer_address_1 _token.token_address = self.test_token_address_4 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval(issuer-1 token-1) diff --git a/tests/test_app_routers_share_transfer_approvals_{token_address}_GET.py b/tests/app/test_share_transfer_approvals_{token_address}_GET.py similarity index 99% rename from tests/test_app_routers_share_transfer_approvals_{token_address}_GET.py rename to tests/app/test_share_transfer_approvals_{token_address}_GET.py index e3eec139..27a41441 100644 --- a/tests/test_app_routers_share_transfer_approvals_{token_address}_GET.py +++ b/tests/app/test_share_transfer_approvals_{token_address}_GET.py @@ -110,7 +110,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -136,7 +136,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -330,7 +330,7 @@ def test_normal_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -450,7 +450,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -654,7 +654,7 @@ def test_normal_4_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -767,7 +767,7 @@ def test_normal_4_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -881,7 +881,7 @@ def test_normal_4_3_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -1027,7 +1027,7 @@ def test_normal_4_3_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -1173,7 +1173,7 @@ def test_normal_4_3_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -1407,7 +1407,7 @@ def test_normal_4_3_4(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -1627,7 +1627,7 @@ def test_normal_4_3_5(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -1879,7 +1879,7 @@ def test_normal_5_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2079,7 +2079,7 @@ def test_normal_5_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2319,7 +2319,7 @@ def test_normal_5_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2559,7 +2559,7 @@ def test_normal_5_4(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2764,7 +2764,7 @@ def test_normal_5_5(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -2967,7 +2967,7 @@ def test_normal_5_6(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -3206,7 +3206,7 @@ def test_normal_5_7(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -3452,7 +3452,7 @@ def test_normal_5_8(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -3694,7 +3694,7 @@ def test_normal_5_9(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -4132,25 +4132,23 @@ def test_error_1_1(self, client, db): "meta": {"code": 1, "title": "RequestValidationError"}, "detail": [ { - "input": "a", + "type": "enum", "loc": ["query", "status", 0], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", - "type": "int_parsing", + "msg": "Input should be 0, 1, 2 or 3", + "input": "a", + "ctx": {"expected": "0, 1, 2 or 3"}, }, { - "input": "c", - "loc": ["query", "offset"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", "type": "int_parsing", + "loc": ["query", "offset"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "c", }, { - "input": "d", - "loc": ["query", "limit"], - "msg": "Input should be a valid integer, unable to parse string " - "as an integer", "type": "int_parsing", + "loc": ["query", "limit"], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "d", }, ], } @@ -4175,7 +4173,7 @@ def test_error_1_2(self, client, db): "detail": [ { "ctx": {"expected": "0, 1, 2 or 3"}, - "input": -1, + "input": "-1", "loc": ["query", "status", 0], "msg": "Input should be 0, 1, 2 or 3", "type": "enum", @@ -4203,7 +4201,7 @@ def test_error_1_3(self, client, db): "detail": [ { "ctx": {"expected": "0, 1, 2 or 3"}, - "input": 4, + "input": "4", "loc": ["query", "status", 0], "msg": "Input should be 0, 1, 2 or 3", "type": "enum", @@ -4237,7 +4235,7 @@ def test_error_2(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_share_transfer_approvals_{token_address}_{id}_GET.py b/tests/app/test_share_transfer_approvals_{token_address}_{id}_GET.py similarity index 98% rename from tests/test_app_routers_share_transfer_approvals_{token_address}_{id}_GET.py rename to tests/app/test_share_transfer_approvals_{token_address}_{id}_GET.py index 96601a41..76d249cd 100644 --- a/tests/test_app_routers_share_transfer_approvals_{token_address}_{id}_GET.py +++ b/tests/app/test_share_transfer_approvals_{token_address}_{id}_GET.py @@ -96,7 +96,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -209,7 +209,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -274,7 +274,7 @@ def test_normal_2_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -384,7 +384,7 @@ def test_normal_2_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -496,7 +496,7 @@ def test_normal_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -610,7 +610,7 @@ def test_normal_4_1(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -722,7 +722,7 @@ def test_normal_4_2(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXTransferApproval @@ -835,7 +835,7 @@ def test_normal_4_3(self, client, db): _token.issuer_address = self.test_issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) # prepare data: IDXPersonalInfo @@ -982,7 +982,7 @@ def test_error_2(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -1010,7 +1010,7 @@ def test_error_3(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 1 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_share_transfer_approvals_{token_address}_{id}_POST.py b/tests/app/test_share_transfer_approvals_{token_address}_{id}_POST.py similarity index 97% rename from tests/test_app_routers_share_transfer_approvals_{token_address}_{id}_POST.py rename to tests/app/test_share_transfer_approvals_{token_address}_{id}_POST.py index b15f52a7..1c5d9cd7 100644 --- a/tests/test_app_routers_share_transfer_approvals_{token_address}_{id}_POST.py +++ b/tests/app/test_share_transfer_approvals_{token_address}_{id}_POST.py @@ -18,12 +18,11 @@ """ import hashlib -from datetime import datetime +from datetime import UTC, datetime from unittest import mock from unittest.mock import ANY, MagicMock import pytest -from pytz import timezone from sqlalchemy import select import config @@ -49,8 +48,6 @@ from app.utils.e2ee_utils import E2EEUtils from tests.account_config import config_eth_account -local_tz = timezone(config.TZ) - class TestAppRoutersShareTransferApprovalsTokenAddressIdPOST: # target API endpoint @@ -93,7 +90,7 @@ def test_normal_1_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -168,7 +165,10 @@ def test_normal_1_1(self, client, db): assert resp.status_code == 200 assert resp.json() is None - _expected = {"application_id": 100, "data": str(datetime.utcnow().timestamp())} + _expected = { + "application_id": 100, + "data": str(datetime.now(UTC).replace(tzinfo=None).timestamp()), + } mock_transfer.assert_called_once_with( data=ApproveTransferParams(**_expected), @@ -227,7 +227,7 @@ def test_normal_1_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -303,7 +303,10 @@ def test_normal_1_2(self, client, db): assert resp.status_code == 200 assert resp.json() is None - _expected = {"escrow_id": 100, "data": str(datetime.utcnow().timestamp())} + _expected = { + "escrow_id": 100, + "data": str(datetime.now(UTC).replace(tzinfo=None).timestamp()), + } mock_transfer.assert_called_once_with( data=EscrowApproveTransferParams(**_expected), @@ -362,7 +365,7 @@ def test_normal_2_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -437,7 +440,10 @@ def test_normal_2_1(self, client, db): assert resp.status_code == 200 assert resp.json() is None - _expected = {"application_id": 100, "data": str(datetime.utcnow().timestamp())} + _expected = { + "application_id": 100, + "data": str(datetime.now(UTC).replace(tzinfo=None).timestamp()), + } mock_transfer.assert_called_once_with( data=CancelTransferParams(**_expected), @@ -501,7 +507,7 @@ def test_normal_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -576,7 +582,10 @@ def test_normal_3(self, client, db): assert resp.status_code == 200 assert resp.json() is None - _expected = {"application_id": 100, "data": str(datetime.utcnow().timestamp())} + _expected = { + "application_id": 100, + "data": str(datetime.now(UTC).replace(tzinfo=None).timestamp()), + } mock_transfer.assert_called_once_with( data=ApproveTransferParams(**_expected), @@ -832,7 +841,7 @@ def test_error_3_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -877,7 +886,7 @@ def test_error_4_1(self, client, db): _token.token_address = self.test_token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -921,7 +930,7 @@ def test_error_4_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -986,7 +995,7 @@ def test_error_4_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1051,7 +1060,7 @@ def test_error_4_4(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1114,7 +1123,7 @@ def test_error_4_5(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1177,7 +1186,7 @@ def test_error_4_6(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1253,7 +1262,7 @@ def test_error_5_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1346,7 +1355,7 @@ def test_error_5_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1456,7 +1465,7 @@ def test_error_5_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1550,7 +1559,7 @@ def test_error_5_4(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1655,7 +1664,7 @@ def test_error_6_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1748,7 +1757,7 @@ def test_error_6_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1846,7 +1855,7 @@ def test_error_7_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 @@ -1914,7 +1923,7 @@ def test_error_7_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = self.test_token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) id = 10 diff --git a/tests/test_app_routers_share_transfers_POST.py b/tests/app/test_share_transfers_POST.py similarity index 99% rename from tests/test_app_routers_share_transfers_POST.py rename to tests/app/test_share_transfers_POST.py index 1f8e5c50..a0de6e71 100644 --- a/tests/test_app_routers_share_transfers_POST.py +++ b/tests/app/test_share_transfers_POST.py @@ -65,7 +65,7 @@ def test_normal_1(self, IbetShareContract_mock, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -140,7 +140,7 @@ def test_normal_2(self, IbetShareContract_mock, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -547,7 +547,7 @@ def test_error_9(self, client, db): token.token_address = _token_address token.abi = "" token.token_status = 0 - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -607,7 +607,7 @@ def test_error_10(self, client, db): token.issuer_address = _admin_address token.token_address = _token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/app/test_share_transfers_{token_address}_GET.py b/tests/app/test_share_transfers_{token_address}_GET.py new file mode 100644 index 00000000..7c4fe4b3 --- /dev/null +++ b/tests/app/test_share_transfers_{token_address}_GET.py @@ -0,0 +1,1950 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from datetime import datetime + +from pytz import timezone + +import config +from app.model.db import ( + IDXPersonalInfo, + IDXTransfer, + IDXTransferSourceEventType, + Token, + TokenType, + TokenVersion, +) + +local_tz = timezone(config.TZ) + + +class TestAppRoutersShareTransfersGET: + # target API endpoint + base_url = "/share/transfers/{}" + + test_transaction_hash = "test_transaction_hash" + test_issuer_address = "test_issuer_address" + test_token_address = "test_token_address" + + test_from_address_1 = "test_from_address_1" + test_from_address_2 = "test_from_address_2" + test_to_address_1 = "test_to_address_1" + test_to_address_2 = "test_to_address_2" + + test_block_timestamp = [ + datetime.strptime("2022/01/02 15:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/03 + datetime.strptime("2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/02 + datetime.strptime("2022/01/02 00:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/02 + ] + test_block_timestamp_str = [ + "2022-01-03T00:20:30+09:00", + "2022-01-02T00:20:30+09:00", + "2022-01-02T09:20:30+09:00", + ] + + ########################################################################### + # Normal Case + ########################################################################### + + # + # default sort + def test_normal_1(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + for i in range(0, 3): + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = i + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[i] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get(self.base_url.format(self.test_token_address)) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # offset, limit + def test_normal_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_from_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_to_address_1 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + # prepare data: IDXTransfer + for i in range(0, 3): + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = i + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[i] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address) + "?offset=1&limit=1" + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": 1, "limit": 1, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": { + "address": "address_test1", + "birth": "birth_test1", + "email": "email_test1", + "is_corporate": False, + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "tax_category": 10, + }, + "to_address": self.test_to_address_1, + "to_address_personal_information": { + "address": "address_test2", + "birth": "birth_test2", + "email": "email_test2", + "is_corporate": False, + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "tax_category": 10, + }, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: block_timestamp_from + def test_normal_3_1(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"block_timestamp_from": "2022-01-02T09:20:30"}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: block_timestamp_to + def test_normal_3_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"block_timestamp_to": "2022-01-02T09:20:30"}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: from_address + def test_normal_3_3(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"from_address": self.test_from_address_1}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: to_address + def test_normal_3_4(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"to_address": self.test_to_address_1}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: from_address_name + def test_normal_3_5(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_from_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_from_address_2 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"from_address_name": "テスト太郎1"}, # test_from_address_1's name + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 2}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: to_address_name + def test_normal_3_6(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_to_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_to_address_2 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"to_address_name": "テスト太郎1"}, # test_from_address_1's name + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 2}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: amount (EQUAL) + def test_normal_3_7_1(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 10 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 20 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 30 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"amount": 20}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 20, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: amount (GTE) + def test_normal_3_7_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 10 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 20 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 30 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"amount": 20, "amount_operator": 1}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 30, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 20, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: amount (LTE) + def test_normal_3_7_3(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 10 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 20 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 30 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"amount": 20, "amount_operator": 2}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 10, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 20, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # filter: source_event + def test_normal_3_8(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"source_event": IDXTransferSourceEventType.UNLOCK.value}, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + } + ], + } + assert resp.json() == assumed_response + + # + # filter: data + def test_normal_3_9(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), params={"data": "unlo"} + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + } + ], + } + assert resp.json() == assumed_response + + # + # sort: block_timestamp ASC + def test_normal_4_1(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + for i in range(0, 3): + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = i + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[i] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "block_timestamp", + "sort_order": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: from_address ASC + def test_normal_4_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "from_address", + "sort_order": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_2, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_2, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: to_address DESC + def test_normal_4_3(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "to_address", + "sort_order": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_2, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: from_address_name DESC + def test_normal_4_4(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_2 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_from_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_from_address_2 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "from_address_name", + "sort_order": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 2}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_2, + "from_address_personal_information": { + "address": "address_test2", + "birth": "birth_test2", + "email": "email_test2", + "is_corporate": False, + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "tax_category": 10, + }, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": { + "address": "address_test1", + "birth": "birth_test1", + "email": "email_test1", + "is_corporate": False, + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "tax_category": 10, + }, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: to_address_name DESC + def test_normal_4_5(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 0 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_2 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + # prepare data: IDXPersonalInfo + _personal_info_from = IDXPersonalInfo() + _personal_info_from.account_address = self.test_to_address_1 + _personal_info_from.issuer_address = self.test_issuer_address + _personal_info_from._personal_info = { + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_from) + + _personal_info_to = IDXPersonalInfo() + _personal_info_to.account_address = self.test_to_address_2 + _personal_info_to.issuer_address = self.test_issuer_address + _personal_info_to._personal_info = { + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } # latest data + db.add(_personal_info_to) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "to_address_name", + "sort_order": 1, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 2}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_2, + "to_address_personal_information": { + "address": "address_test2", + "birth": "birth_test2", + "email": "email_test2", + "is_corporate": False, + "key_manager": "key_manager_test2", + "name": "テスト太郎2", + "postal_code": "postal_code_test2", + "tax_category": 10, + }, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": { + "address": "address_test1", + "birth": "birth_test1", + "email": "email_test1", + "is_corporate": False, + "key_manager": "key_manager_test1", + "name": "テスト太郎1", + "postal_code": "postal_code_test1", + "tax_category": 10, + }, + "amount": 0, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + ], + } + assert resp.json() == assumed_response + + # + # sort: amount DESC + def test_normal_4_6(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.version = TokenVersion.V_24_06 + db.add(_token) + + # prepare data: IDXTransfer + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 1 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[0] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value + _idx_transfer.data = None + _idx_transfer.block_timestamp = self.test_block_timestamp[1] + db.add(_idx_transfer) + + _idx_transfer = IDXTransfer() + _idx_transfer.transaction_hash = self.test_transaction_hash + _idx_transfer.token_address = self.test_token_address + _idx_transfer.from_address = self.test_from_address_1 + _idx_transfer.to_address = self.test_to_address_1 + _idx_transfer.amount = 2 + _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value + _idx_transfer.data = {"message": "unlock"} + _idx_transfer.block_timestamp = self.test_block_timestamp[2] + db.add(_idx_transfer) + + db.commit() + + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={ + "sort_item": "amount", + "sort_order": 0, + }, + ) + + # assertion + assert resp.status_code == 200 + assumed_response = { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "transfer_history": [ + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 1, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[0], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.UNLOCK.value, + "data": {"message": "unlock"}, + "block_timestamp": self.test_block_timestamp_str[2], + }, + { + "transaction_hash": self.test_transaction_hash, + "token_address": self.test_token_address, + "from_address": self.test_from_address_1, + "from_address_personal_information": None, + "to_address": self.test_to_address_1, + "to_address_personal_information": None, + "amount": 2, + "source_event": IDXTransferSourceEventType.TRANSFER.value, + "data": None, + "block_timestamp": self.test_block_timestamp_str[1], + }, + ], + } + assert resp.json() == assumed_response + + ########################################################################### + # Error Case + ########################################################################### + + # + # token not found + def test_error_1(self, client, db): + # request target API + resp = client.get(self.base_url.format(self.test_token_address)) + + # assertion + assert resp.status_code == 404 + assumed_response = { + "meta": {"code": 1, "title": "NotFound"}, + "detail": "token not found", + } + assert resp.json() == assumed_response + + # + # processing token + def test_error_2(self, client, db): + # prepare data: Token + _token = Token() + _token.type = TokenType.IBET_SHARE.value + _token.tx_hash = self.test_transaction_hash + _token.issuer_address = self.test_issuer_address + _token.token_address = self.test_token_address + _token.abi = {} + _token.token_status = 0 + _token.version = TokenVersion.V_24_06 + db.add(_token) + + db.commit() + + # request target API + resp = client.get(self.base_url.format(self.test_token_address)) + + # assertion + assert resp.status_code == 400 + assert resp.json() == { + "meta": {"code": 1, "title": "InvalidParameterError"}, + "detail": "this token is temporarily unavailable", + } + + # + # param error: sort_item + def test_error_3(self, client, db): + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), + params={"sort_item": "block_timestamp12345"}, + ) + + # assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "enum", + "loc": ["query", "sort_item"], + "msg": "Input should be 'block_timestamp', 'from_address', 'to_address', 'from_address_name', 'to_address_name' or 'amount'", + "input": "block_timestamp12345", + "ctx": { + "expected": "'block_timestamp', 'from_address', 'to_address', 'from_address_name', 'to_address_name' or 'amount'" + }, + } + ], + } + + # + # param error: sort_order(min) + def test_error_4(self, client, db): + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), params={"sort_order": -1} + ) + + # assertion + assert resp.status_code == 422 + assumed_response = { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "ctx": {"expected": "0 or 1"}, + "input": "-1", + "loc": ["query", "sort_order"], + "msg": "Input should be 0 or 1", + "type": "enum", + } + ], + } + assert resp.json() == assumed_response + + # + # param error: sort_order(max) + def test_error_5(self, client, db): + # request target API + resp = client.get( + self.base_url.format(self.test_token_address), params={"sort_order": 2} + ) + + # assertion + assert resp.status_code == 422 + assumed_response = { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "ctx": {"expected": "0 or 1"}, + "input": "2", + "loc": ["query", "sort_order"], + "msg": "Input should be 0 or 1", + "type": "enum", + } + ], + } + assert resp.json() == assumed_response diff --git a/tests/app/test_token_holders_personal_info_GET.py b/tests/app/test_token_holders_personal_info_GET.py new file mode 100644 index 00000000..52d28420 --- /dev/null +++ b/tests/app/test_token_holders_personal_info_GET.py @@ -0,0 +1,1115 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from unittest import mock + +import pytest + +from app.model.db import IDXPersonalInfo +from tests.account_config import config_eth_account + + +class TestListTokenHoldersPersonalInfo: + # target API endpoint + url = "/token/holders/personal_info" + + ########################################################################### + # Normal Case + ########################################################################### + + # + # 0 record + def test_normal_1(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 0, "limit": None, "offset": None, "total": 0}, + "personal_info": [], + } + + # + # 1 record + @pytest.mark.freeze_time("2024-05-13 12:34:56") + def test_normal_2(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(personal_info_idx) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 1}, + "personal_info": [ + { + "id": 1, + "account_address": account_address1, + "personal_info": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:34:56+09:00", + "modified": "2024-05-13T21:34:56+09:00", + } + ], + } + + # + # Multiple records + # No search filter + @pytest.mark.freeze_time("2024-05-13 12:34:56") + def test_normal_3(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + account_address2 = "account_address2" + account_address3 = "account_address3" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address2 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + # "birth": None, + "is_corporate": False, + "tax_category": 10, + } + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address3 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + } + db.add(personal_info_idx) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 1, + "account_address": account_address1, + "personal_info": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:34:56+09:00", + "modified": "2024-05-13T21:34:56+09:00", + }, + { + "id": 2, + "account_address": account_address2, + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:34:56+09:00", + "modified": "2024-05-13T21:34:56+09:00", + }, + { + "id": 3, + "account_address": account_address3, + "personal_info": { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:34:56+09:00", + "modified": "2024-05-13T21:34:56+09:00", + }, + ], + } + + # + # Multiple records + # Search filter: account_address + def test_normal_4_1(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + account_address2 = "account_address2" + account_address3 = "account_address3" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 09:00:00" + personal_info_idx.modified = "2024-05-14 09:00:00" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address2 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + # "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 12:00:00" + personal_info_idx.modified = "2024-05-14 12:00:00" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address3 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 15:00:00" + personal_info_idx.modified = "2024-05-14 15:00:00" + db.add(personal_info_idx) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + params={ + "account_address": account_address2, + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 2, + "account_address": account_address2, + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:00:00+09:00", + "modified": "2024-05-14T21:00:00+09:00", + }, + ], + } + + # + # Multiple records + # Search filter: created_from, created_to + def test_normal_4_2(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + account_address2 = "account_address2" + account_address3 = "account_address3" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 09:00:00" + personal_info_idx.modified = "2024-05-14 09:00:00" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address2 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + # "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 12:00:00" + personal_info_idx.modified = "2024-05-14 12:00:00" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address3 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 15:00:00" + personal_info_idx.modified = "2024-05-14 15:00:00" + db.add(personal_info_idx) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + params={ + "created_from": "2024-05-13 21:00:00", + "created_to": "2024-05-13 21:00:00", + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 2, + "account_address": account_address2, + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:00:00+09:00", + "modified": "2024-05-14T21:00:00+09:00", + }, + ], + } + + # + # Multiple records + # Search filter: modified_from, modified_to + def test_normal_4_3(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + account_address2 = "account_address2" + account_address3 = "account_address3" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 09:00:00" + personal_info_idx.modified = "2024-05-14 09:00:00" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address2 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + # "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 12:00:00" + personal_info_idx.modified = "2024-05-14 12:00:00" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address3 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2024-05-13 15:00:00" + personal_info_idx.modified = "2024-05-14 15:00:00" + db.add(personal_info_idx) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + params={ + "modified_from": "2024-05-14 21:00:00", + "modified_to": "2024-05-14 21:00:00", + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 2, + "account_address": account_address2, + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:00:00+09:00", + "modified": "2024-05-14T21:00:00+09:00", + }, + ], + } + + # + # Sort + # - sort_item: None(created) + # - sort_order: DESC + def test_normal_5_1(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + account_address2 = "account_address2" + account_address3 = "account_address3" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:00" + personal_info_idx.modified = "2023-10-24 00:00:00" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address2 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + # "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:01" + personal_info_idx.modified = "2023-10-24 00:00:01" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address3 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:02" + personal_info_idx.modified = "2023-10-24 00:00:02" + db.add(personal_info_idx) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + params={"sort_order": 1}, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 3, + "account_address": account_address3, + "personal_info": { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2023-10-23T09:00:02+09:00", + "modified": "2023-10-24T09:00:02+09:00", + }, + { + "id": 2, + "account_address": account_address2, + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2023-10-23T09:00:01+09:00", + "modified": "2023-10-24T09:00:01+09:00", + }, + { + "id": 1, + "account_address": account_address1, + "personal_info": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "created": "2023-10-23T09:00:00+09:00", + "modified": "2023-10-24T09:00:00+09:00", + }, + ], + } + + # + # Sort + # - sort_item: account_address + # - sort_order: ASC + def test_normal_5_2(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + account_address2 = "account_address2" + account_address3 = "account_address3" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:02" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address2 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + # "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:01" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address3 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:00" + db.add(personal_info_idx) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + params={"sort_item": "account_address", "sort_order": 0}, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 1, + "account_address": account_address1, + "personal_info": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "created": mock.ANY, + "modified": mock.ANY, + }, + { + "id": 2, + "account_address": account_address2, + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": mock.ANY, + "modified": mock.ANY, + }, + { + "id": 3, + "account_address": account_address3, + "personal_info": { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": mock.ANY, + "modified": mock.ANY, + }, + ], + } + + # + # Sort + # - sort_item: modified + # - sort_order: ASC + def test_normal_5_3(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + account_address2 = "account_address2" + account_address3 = "account_address3" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:00" + personal_info_idx.modified = "2023-10-24 00:00:02" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address2 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + # "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:01" + personal_info_idx.modified = "2023-10-24 00:00:01" + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address3 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + } + personal_info_idx.created = "2023-10-23 00:00:02" + personal_info_idx.modified = "2023-10-24 00:00:00" + db.add(personal_info_idx) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + params={"sort_item": "modified", "sort_order": 0}, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 3, + "account_address": account_address3, + "personal_info": { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2023-10-23T09:00:02+09:00", + "modified": "2023-10-24T09:00:00+09:00", + }, + { + "id": 2, + "account_address": account_address2, + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2023-10-23T09:00:01+09:00", + "modified": "2023-10-24T09:00:01+09:00", + }, + { + "id": 1, + "account_address": account_address1, + "personal_info": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "created": "2023-10-23T09:00:00+09:00", + "modified": "2023-10-24T09:00:02+09:00", + }, + ], + } + + # + # Pagination + @pytest.mark.freeze_time("2024-05-13 12:34:56") + def test_normal_6(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + account_address1 = "account_address1" + account_address2 = "account_address2" + account_address3 = "account_address3" + + # prepare data + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address1 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address2 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + # "birth": None, + "is_corporate": False, + "tax_category": 10, + } + db.add(personal_info_idx) + + personal_info_idx = IDXPersonalInfo() + personal_info_idx.issuer_address = issuer_address + personal_info_idx.account_address = account_address3 + personal_info_idx.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + } + db.add(personal_info_idx) + + db.commit() + + # request target API + req_param = {"limit": 2, "offset": 1} + resp = client.get( + self.url, + params=req_param, + headers={ + "issuer-address": issuer_address, + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "limit": 2, "offset": 1, "total": 3}, + "personal_info": [ + { + "id": 2, + "account_address": account_address2, + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:34:56+09:00", + "modified": "2024-05-13T21:34:56+09:00", + }, + { + "id": 3, + "account_address": account_address3, + "personal_info": { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": None, + "is_corporate": False, + "tax_category": 10, + }, + "created": "2024-05-13T21:34:56+09:00", + "modified": "2024-05-13T21:34:56+09:00", + }, + ], + } + + ########################################################################### + # Error Case + ########################################################################### + + # + # RequestValidationError: created_from, created_to, modified_from, modified_to + def test_error_1_1(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + params={ + "created_from": "invalid_date", + "created_to": "invalid_date", + "modified_from": "invalid_date", + "modified_to": "invalid_date", + }, + ) + + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "value_error", + "loc": ["query", "created_from"], + "msg": "Value error, value must be of string datetime format", + "input": "invalid_date", + "ctx": {"error": {}}, + }, + { + "type": "value_error", + "loc": ["query", "created_to"], + "msg": "Value error, value must be of string datetime format", + "input": "invalid_date", + "ctx": {"error": {}}, + }, + { + "type": "value_error", + "loc": ["query", "modified_from"], + "msg": "Value error, value must be of string datetime format", + "input": "invalid_date", + "ctx": {"error": {}}, + }, + { + "type": "value_error", + "loc": ["query", "modified_to"], + "msg": "Value error, value must be of string datetime format", + "input": "invalid_date", + "ctx": {"error": {}}, + }, + ], + } + + # + # RequestValidationError: sort_item, sort_order + def test_error_1_2(self, client, db): + issuer_account = config_eth_account("user1") + issuer_address = issuer_account["address"] + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": issuer_address, + }, + params={"sort_item": "invalid_sort_item", "sort_order": 2}, + ) + + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "enum", + "loc": ["query", "sort_item"], + "msg": "Input should be 'account_address', 'created' or 'modified'", + "input": "invalid_sort_item", + "ctx": {"expected": "'account_address', 'created' or 'modified'"}, + }, + { + "type": "enum", + "loc": ["query", "sort_order"], + "msg": "Input should be 0 or 1", + "input": "2", + "ctx": {"expected": "0 or 1"}, + }, + ], + } diff --git a/tests/app/test_token_holders_personal_info_history_GET.py b/tests/app/test_token_holders_personal_info_history_GET.py new file mode 100644 index 00000000..866b89b6 --- /dev/null +++ b/tests/app/test_token_holders_personal_info_history_GET.py @@ -0,0 +1,829 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from app.model.db import IDXPersonalInfoHistory, PersonalInfoEventType +from tests.account_config import config_eth_account + + +class TestListTokenHoldersPersonalInfoHistory: + # target API endpoint + url = "/token/holders/personal_info/history" + + test_issuer_address_1 = config_eth_account("user1")["address"] + test_issuer_address_2 = config_eth_account("user2")["address"] + test_account_address_1 = config_eth_account("user3")["address"] + test_account_address_2 = config_eth_account("user4")["address"] + + ########################################################################### + # Normal Case + ########################################################################### + + # + # 0 record + def test_normal_1(self, client, db): + # Request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 0, "limit": None, "offset": None, "total": 0}, + "personal_info": [], + } + + # + # Multiple records + # No search filter + def test_normal_2(self, client, db): + # Prepare data + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-12 23:59:59" + history.created = "2024-05-13 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.MODIFY + history.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-13 23:59:59" + history.created = "2024-05-14 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_2 + history.account_address = self.test_account_address_2 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-13 23:59:59" + history.created = "2024-05-14 00:00:00" + db.add(history) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "limit": None, "offset": None, "total": 2}, + "personal_info": [ + { + "id": 1, + "account_address": self.test_account_address_1, + "event_type": "register", + "personal_info": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-13T08:59:59+09:00", + "created": "2024-05-13T09:00:00+09:00", + }, + { + "id": 2, + "account_address": self.test_account_address_1, + "event_type": "modify", + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T08:59:59+09:00", + "created": "2024-05-14T09:00:00+09:00", + }, + ], + } + + # + # Multiple records + # Search filter: account_address + def test_normal_3_1(self, client, db): + # Prepare data + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-12 23:59:59" + history.created = "2024-05-13 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.MODIFY + history.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-13 23:59:59" + history.created = "2024-05-14 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_2 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-13 23:59:59" + history.created = "2024-05-14 00:00:00" + db.add(history) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + params={"account_address": self.test_account_address_1}, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 1, + "account_address": self.test_account_address_1, + "event_type": "register", + "personal_info": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-13T08:59:59+09:00", + "created": "2024-05-13T09:00:00+09:00", + }, + { + "id": 2, + "account_address": self.test_account_address_1, + "event_type": "modify", + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T08:59:59+09:00", + "created": "2024-05-14T09:00:00+09:00", + }, + ], + } + + # + # Multiple records + # Search filter: event_type + def test_normal_3_2(self, client, db): + # Prepare data + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-12 23:59:59" + history.created = "2024-05-13 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.MODIFY + history.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:00" + history.created = "2024-05-14 00:00:01" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:01" + history.created = "2024-05-14 00:00:02" + db.add(history) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + params={ + "event_type": "modify", + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 2, + "account_address": self.test_account_address_1, + "event_type": "modify", + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T09:00:00+09:00", + "created": "2024-05-14T09:00:01+09:00", + }, + ], + } + + # + # Multiple records + # Search filter: created_from, created_to + def test_normal_3_3(self, client, db): + # Prepare data + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-12 23:59:59" + history.created = "2024-05-13 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.MODIFY + history.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:00" + history.created = "2024-05-14 00:00:01" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:01" + history.created = "2024-05-14 00:00:02" + db.add(history) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + params={ + "created_from": "2024-05-14 09:00:01", + "created_to": "2024-05-14 09:00:01", + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 2, + "account_address": self.test_account_address_1, + "event_type": "modify", + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T09:00:00+09:00", + "created": "2024-05-14T09:00:01+09:00", + }, + ], + } + + # + # Multiple records + # Search filter: block_timestamp_from, block_timestamp_to + def test_normal_3_4(self, client, db): + # Prepare data + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-12 23:59:59" + history.created = "2024-05-13 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.MODIFY + history.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:00" + history.created = "2024-05-14 00:00:01" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:01" + history.created = "2024-05-14 00:00:02" + db.add(history) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + params={ + "block_timestamp_from": "2024-05-14 09:00:00", + "block_timestamp_to": "2024-05-14 09:00:00", + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 2, + "account_address": self.test_account_address_1, + "event_type": "modify", + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T09:00:00+09:00", + "created": "2024-05-14T09:00:01+09:00", + }, + ], + } + + # + # Multiple records + # Sort: block_timestamp, DESC + def test_normal_4(self, client, db): + # Prepare data + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-13 23:59:59" + history.created = "2024-05-14 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.MODIFY + history.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:00" + history.created = "2024-05-14 00:00:01" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:01" + history.created = "2024-05-14 00:00:02" + db.add(history) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + params={ + "sort_order": 1, + }, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "limit": None, "offset": None, "total": 3}, + "personal_info": [ + { + "id": 3, + "account_address": self.test_account_address_1, + "event_type": "register", + "personal_info": { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T09:00:01+09:00", + "created": "2024-05-14T09:00:02+09:00", + }, + { + "id": 2, + "account_address": self.test_account_address_1, + "event_type": "modify", + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T09:00:00+09:00", + "created": "2024-05-14T09:00:01+09:00", + }, + { + "id": 1, + "account_address": self.test_account_address_1, + "event_type": "register", + "personal_info": { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T08:59:59+09:00", + "created": "2024-05-14T09:00:00+09:00", + }, + ], + } + + # + # Multiple records + # Pagination: offset, limit + def test_normal_5(self, client, db): + # Prepare data + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test1", + "name": "name_test1", + "postal_code": "postal_code_test1", + "address": "address_test1", + "email": "email_test1", + "birth": "birth_test1", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-12 23:59:59" + history.created = "2024-05-14 00:00:00" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.MODIFY + history.personal_info = { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:00" + history.created = "2024-05-14 00:00:01" + db.add(history) + + history = IDXPersonalInfoHistory() + history.issuer_address = self.test_issuer_address_1 + history.account_address = self.test_account_address_1 + history.event_type = PersonalInfoEventType.REGISTER + history.personal_info = { + "key_manager": "key_manager_test3", + "name": "name_test3", + "postal_code": "postal_code_test3", + "address": "address_test3", + "email": "email_test3", + "birth": "birth_test3", + "is_corporate": False, + "tax_category": 10, + } + history.block_timestamp = "2024-05-14 00:00:01" + history.created = "2024-05-14 00:00:02" + db.add(history) + + db.commit() + + # request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + params={"offset": 1, "limit": 1}, + ) + + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "limit": 1, "offset": 1, "total": 3}, + "personal_info": [ + { + "id": 2, + "account_address": self.test_account_address_1, + "event_type": "modify", + "personal_info": { + "key_manager": "key_manager_test2", + "name": "name_test2", + "postal_code": "postal_code_test2", + "address": "address_test2", + "email": "email_test2", + "birth": "birth_test2", + "is_corporate": False, + "tax_category": 10, + }, + "block_timestamp": "2024-05-14T09:00:00+09:00", + "created": "2024-05-14T09:00:01+09:00", + }, + ], + } + + ########################################################################### + # Error Case + ########################################################################### + + # + # RequestValidationError: created_from, created_to + def test_error_1(self, client, db): + # Request target API + resp = client.get( + self.url, + headers={ + "issuer-address": self.test_issuer_address_1, + }, + params={ + "created_from": "invalid_date", + "created_to": "invalid_date", + }, + ) + + # Assertion + assert resp.status_code == 422 + assert resp.json() == { + "meta": {"code": 1, "title": "RequestValidationError"}, + "detail": [ + { + "type": "value_error", + "loc": ["query", "created_from"], + "msg": "Value error, value must be of string datetime format", + "input": "invalid_date", + "ctx": {"error": {}}, + }, + { + "type": "value_error", + "loc": ["query", "created_to"], + "msg": "Value error, value must be of string datetime format", + "input": "invalid_date", + "ctx": {"error": {}}, + }, + ], + } diff --git a/tests/test_app_routers_token_holders_{token_address}_GET.py b/tests/app/test_token_holders_{token_address}_GET.py similarity index 96% rename from tests/test_app_routers_token_holders_{token_address}_GET.py rename to tests/app/test_token_holders_{token_address}_GET.py index 806530c7..6b3ec4bc 100644 --- a/tests/test_app_routers_token_holders_{token_address}_GET.py +++ b/tests/app/test_token_holders_{token_address}_GET.py @@ -18,6 +18,7 @@ """ import uuid +from datetime import datetime from unittest import mock from app.model.db import ( @@ -52,7 +53,7 @@ def test_normal_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -80,7 +81,7 @@ def test_normal_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) token_holder_list1 = TokenHoldersList() @@ -123,7 +124,7 @@ def test_normal_3_1(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) token_holder_list1 = TokenHoldersList() @@ -225,7 +226,7 @@ def test_normal_3_2(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() @@ -332,7 +333,7 @@ def test_normal_3_3(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) token_holder_list1 = TokenHoldersList() @@ -406,7 +407,7 @@ def test_normal_4(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) token_holder_list1 = TokenHoldersList() @@ -414,6 +415,7 @@ def test_normal_4(self, client, db): token_holder_list1.list_id = str(uuid.uuid4()) token_holder_list1.block_number = 100 token_holder_list1.batch_status = TokenHolderBatchStatus.PENDING.value + token_holder_list1.created = datetime(2024, 1, 2, 3, 4, 51) db.add(token_holder_list1) token_holder_list2 = TokenHoldersList() @@ -421,6 +423,7 @@ def test_normal_4(self, client, db): token_holder_list2.list_id = str(uuid.uuid4()) token_holder_list2.block_number = 200 token_holder_list2.batch_status = TokenHolderBatchStatus.FAILED.value + token_holder_list2.created = datetime(2024, 1, 2, 3, 4, 52) db.add(token_holder_list2) token_holder_list3 = TokenHoldersList() @@ -428,6 +431,7 @@ def test_normal_4(self, client, db): token_holder_list3.list_id = str(uuid.uuid4()) token_holder_list3.block_number = 300 token_holder_list3.batch_status = TokenHolderBatchStatus.FAILED.value + token_holder_list3.created = datetime(2024, 1, 2, 3, 4, 53) db.add(token_holder_list3) token_holder_list4 = TokenHoldersList() @@ -435,6 +439,7 @@ def test_normal_4(self, client, db): token_holder_list4.list_id = str(uuid.uuid4()) token_holder_list4.block_number = 400 token_holder_list4.batch_status = TokenHolderBatchStatus.DONE.value + token_holder_list4.created = datetime(2024, 1, 2, 3, 4, 54) db.add(token_holder_list4) token_holder_list5 = TokenHoldersList() @@ -442,6 +447,7 @@ def test_normal_4(self, client, db): token_holder_list5.list_id = str(uuid.uuid4()) token_holder_list5.block_number = 500 token_holder_list5.batch_status = TokenHolderBatchStatus.DONE.value + token_holder_list5.created = datetime(2024, 1, 2, 3, 4, 55) db.add(token_holder_list5) db.commit() @@ -483,7 +489,7 @@ def test_normal_5(self, client, db): token.issuer_address = issuer_address token.token_address = token_address token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) token_holder_list1 = TokenHoldersList() @@ -627,7 +633,7 @@ def test_error_3(self, client, db): token.token_address = token_address token.token_status = 0 token.abi = "" - token.version = TokenVersion.V_22_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_app_routers_token_holders_{token_address}_collection_POST.py b/tests/app/test_token_holders_{token_address}_collection_POST.py similarity index 96% rename from tests/test_app_routers_token_holders_{token_address}_collection_POST.py rename to tests/app/test_token_holders_{token_address}_collection_POST.py index 86eba5ed..b7ae2318 100644 --- a/tests/test_app_routers_token_holders_{token_address}_collection_POST.py +++ b/tests/app/test_token_holders_{token_address}_collection_POST.py @@ -24,7 +24,7 @@ import pytest from sqlalchemy import select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.model.db import ( @@ -37,7 +37,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) class TestAppRoutersHoldersTokenAddressCollectionPOST: @@ -64,7 +64,7 @@ async def test_normal_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -116,7 +116,7 @@ async def test_normal_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -184,7 +184,7 @@ async def test_error_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -228,7 +228,7 @@ async def test_error_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -266,7 +266,7 @@ async def test_error_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -311,7 +311,7 @@ def test_error_4(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -355,7 +355,7 @@ def test_error_5(self, client, db): _token1.issuer_address = issuer_address _token1.token_address = token_address1 _token1.abi = {} - _token1.version = TokenVersion.V_22_12 + _token1.version = TokenVersion.V_24_06 db.add(_token1) db.commit() @@ -391,7 +391,7 @@ def test_error_5(self, client, db): _token2.issuer_address = issuer_address _token2.token_address = token_address2 _token2.abi = {} - _token2.version = TokenVersion.V_22_12 + _token2.version = TokenVersion.V_24_06 db.add(_token2) db.commit() @@ -432,7 +432,7 @@ async def test_error_6(self, client, db): _token.token_address = token_address _token.abi = {} _token.token_status = 0 - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -471,7 +471,7 @@ async def test_error_7(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() diff --git a/tests/test_app_routers_token_holders_{token_address}_collection_{collection_id}_GET.py b/tests/app/test_token_holders_{token_address}_collection_{collection_id}_GET.py similarity index 96% rename from tests/test_app_routers_token_holders_{token_address}_collection_{collection_id}_GET.py rename to tests/app/test_token_holders_{token_address}_collection_{collection_id}_GET.py index 8f147eea..0d897d73 100644 --- a/tests/test_app_routers_token_holders_{token_address}_collection_{collection_id}_GET.py +++ b/tests/app/test_token_holders_{token_address}_collection_{collection_id}_GET.py @@ -22,7 +22,7 @@ from sqlalchemy import select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware import config from app.model.db import ( @@ -36,7 +36,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(config.WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) class TestAppRoutersHoldersTokenAddressCollectionIdGET: @@ -64,7 +64,7 @@ def test_normal_1(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) list_id = str(uuid.uuid4()) @@ -107,7 +107,7 @@ def test_normal_2(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) list_id = str(uuid.uuid4()) @@ -189,7 +189,7 @@ def test_error_2(self, client, db): # set status pending _token.token_status = 0 _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) list_id = str(uuid.uuid4()) @@ -231,7 +231,7 @@ def test_error_3(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) list_id = str(uuid.uuid4()) @@ -273,7 +273,7 @@ def test_error_4(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) db.commit() @@ -310,7 +310,7 @@ def test_error_5(self, client, db): _token1.issuer_address = issuer_address _token1.token_address = token_address1 _token1.abi = {} - _token1.version = TokenVersion.V_22_12 + _token1.version = TokenVersion.V_24_06 db.add(_token1) _token2 = Token() @@ -319,7 +319,7 @@ def test_error_5(self, client, db): _token2.issuer_address = issuer_address _token2.token_address = token_address2 _token2.abi = {} - _token2.version = TokenVersion.V_22_12 + _token2.version = TokenVersion.V_24_06 db.add(_token2) list_id = str(uuid.uuid4()) @@ -362,7 +362,7 @@ def test_error_6(self, client, db): _token.issuer_address = issuer_address _token.token_address = token_address _token.abi = {} - _token.version = TokenVersion.V_22_12 + _token.version = TokenVersion.V_24_06 db.add(_token) list_id = str(uuid.uuid4()) diff --git a/tests/test_utils_asynccontract_utils.py b/tests/app/utils/test_asynccontract_utils.py similarity index 98% rename from tests/test_utils_asynccontract_utils.py rename to tests/app/utils/test_asynccontract_utils.py index 29a72727..dc019817 100755 --- a/tests/test_utils_asynccontract_utils.py +++ b/tests/app/utils/test_asynccontract_utils.py @@ -26,7 +26,7 @@ from sqlalchemy.orm import Session from web3 import Web3 from web3.exceptions import ContractLogicError, Web3Exception -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.exceptions import ContractRevertError, SendTransactionError from app.model.db import TransactionLock @@ -35,7 +35,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" @@ -276,7 +276,7 @@ async def test_normal_1(self, db: Session): transaction=tx, private_key=self.private_key ) - assert rtn_tx_hash == rtn_receipt["transactionHash"].hex() + assert rtn_tx_hash == rtn_receipt["transactionHash"].to_0x_hex() assert rtn_receipt["status"] == 1 assert rtn_receipt["to"] is None assert rtn_receipt["from"] == self.test_account["address"] @@ -454,7 +454,7 @@ async def test_normal_1(self, db: Session): ) # Send Transaction - tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction.hex()) + tx_hash = web3.eth.send_raw_transaction(signed_tx.raw_transaction.to_0x_hex()) tx_receipt = web3.eth.wait_for_transaction_receipt( transaction_hash=tx_hash, timeout=10 ) diff --git a/tests/test_utils_check_utils.py b/tests/app/utils/test_check_utils.py similarity index 100% rename from tests/test_utils_check_utils.py rename to tests/app/utils/test_check_utils.py diff --git a/tests/test_utils_contract_utils.py b/tests/app/utils/test_contract_utils.py similarity index 98% rename from tests/test_utils_contract_utils.py rename to tests/app/utils/test_contract_utils.py index 54c65de0..8aa5e547 100755 --- a/tests/test_utils_contract_utils.py +++ b/tests/app/utils/test_contract_utils.py @@ -26,7 +26,7 @@ from sqlalchemy.orm import Session from web3 import Web3 from web3.exceptions import ContractLogicError, Web3Exception -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.exceptions import ContractRevertError, SendTransactionError from app.model.db import TransactionLock @@ -35,7 +35,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" @@ -266,7 +266,7 @@ def test_normal_1(self, db: Session): transaction=tx, private_key=self.private_key ) - assert rtn_tx_hash == rtn_receipt["transactionHash"].hex() + assert rtn_tx_hash == rtn_receipt["transactionHash"].to_0x_hex() assert rtn_receipt["status"] == 1 assert rtn_receipt["to"] is None assert rtn_receipt["from"] == self.test_account["address"] @@ -438,7 +438,7 @@ def test_normal_1(self, db: Session): ) # Send Transaction - tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction.hex()) + tx_hash = web3.eth.send_raw_transaction(signed_tx.raw_transaction.to_0x_hex()) tx_receipt = web3.eth.wait_for_transaction_receipt( transaction_hash=tx_hash, timeout=10 ) diff --git a/tests/app/utils/test_ledger_utils.py b/tests/app/utils/test_ledger_utils.py new file mode 100644 index 00000000..afbb34c5 --- /dev/null +++ b/tests/app/utils/test_ledger_utils.py @@ -0,0 +1,2813 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +from datetime import datetime + +import pytest +import pytz +from eth_keyfile import decode_keyfile_json +from sqlalchemy import select +from web3 import Web3 +from web3.middleware import ExtraDataToPOAMiddleware + +from app.model.blockchain import ( + IbetShareContract, + IbetStraightBondContract, + PersonalInfoContract, +) +from app.model.blockchain.tx_params.ibet_share import ( + UpdateParams as IbetShareUpdateParams, +) +from app.model.blockchain.tx_params.ibet_straight_bond import ( + UpdateParams as IbetStraightBondUpdateParams, +) +from app.model.db import ( + UTXO, + Account, + AccountRsaStatus, + IDXPersonalInfo, + Ledger, + LedgerDetailsData, + LedgerDetailsDataType, + LedgerDetailsTemplate, + LedgerTemplate, + Notification, + NotificationType, + Token, + TokenType, + TokenVersion, +) +from app.utils import ledger_utils +from app.utils.contract_utils import ContractUtils +from app.utils.e2ee_utils import E2EEUtils +from config import TZ, WEB3_HTTP_PROVIDER, ZERO_ADDRESS +from tests.account_config import config_eth_account + +web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) + + +async def deploy_bond_token_contract( + issuer_address: str, + issuer_private_key: bytes, + personal_info_contract_address: str, + require_personal_info_registered: bool, +): + arguments = [ + "token.name", + "token.symbol", + 100, + 20, + "JPY", + "token.redemption_date", + 30, + "JPY", + "token.return_date", + "token.return_amount", + "token.purpose", + ] + bond_contrat = IbetStraightBondContract() + contract_address, _, _ = await bond_contrat.create( + arguments, issuer_address, issuer_private_key + ) + + data = IbetStraightBondUpdateParams() + data.personal_info_contract_address = personal_info_contract_address + data.require_personal_info_registered = require_personal_info_registered + await bond_contrat.update(data, issuer_address, issuer_private_key) + + return contract_address + + +async def deploy_share_token_contract( + issuer_address: str, + issuer_private_key: bytes, + personal_info_contract_address: str, + require_personal_info_registered: bool, +): + arguments = [ + "token.name", + "token.symbol", + 100, + 20, + 3, + "token.dividend_record_date", + "token.dividend_payment_date", + "token.cancellation_date", + 200, + ] + share_contract = IbetShareContract() + contract_address, _, _ = await share_contract.create( + arguments, issuer_address, issuer_private_key + ) + + data = IbetShareUpdateParams() + data.personal_info_contract_address = personal_info_contract_address + data.require_personal_info_registered = require_personal_info_registered + await share_contract.update(data, issuer_address, issuer_private_key) + + return contract_address + + +def deploy_personal_info_contract(address: str, private_key: bytes): + contract_address, _, _ = ContractUtils.deploy_contract( + "PersonalInfo", [], address, private_key + ) + return contract_address + + +async def register_personal_info( + contract_address: str, issuer_account: Account, investor_list: list +): + personal_info = PersonalInfoContract(issuer_account, contract_address) + for investor in investor_list: + await personal_info.register_info(investor["address"], investor["data"]) + + +class TestCreateLedger: + ########################################################################### + # Normal Case + ########################################################################### + + # + # Share Token + # require_personal_info_registered: True + # All personal information except issuer one has been registered + # -> "some_personal_info_not_registered" = False + @pytest.mark.asyncio + async def test_normal_1_1_1(self, db, async_db): + issuer = config_eth_account("user5") + issuer_address = issuer["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") + ) + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_2 = user_2["address"] + user_private_key_2 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data: Issuer account settings + _account = Account() + _account.issuer_address = issuer_address + _account.keyfile = issuer["keyfile_json"] + _account.eoa_password = E2EEUtils.encrypt("password") + _account.rsa_private_key = issuer["rsa_private_key"] + _account.rsa_public_key = issuer["rsa_public_key"] + _account.rsa_passphrase = E2EEUtils.encrypt("password") + _account.rsa_status = AccountRsaStatus.SET.value + db.add(_account) + + # Prepare data: Personal info contract + personal_info_contract_address = deploy_personal_info_contract( + issuer_address, issuer_private_key + ) + await register_personal_info( + personal_info_contract_address, + _account, + [ + { + "address": user_address_1, + "private_key": user_private_key_1, + "data": { + "name": "name_test_con_1", + "address": "address_test_con_1", + }, + }, + { + "address": user_address_2, + "private_key": user_private_key_2, + "data": { + "name": "name_test_con_2", + "address": "address_test_con_2", + }, + }, + ], + ) + + # Prepare data: IDXPersonalInfo + # - Only user_1 + # - user_2 information is obtained from contract data + _idx_personal_info_1 = IDXPersonalInfo() + _idx_personal_info_1.account_address = user_address_1 + _idx_personal_info_1.issuer_address = issuer_address + _idx_personal_info_1.personal_info = { + "name": "name_test_db_1", + "address": "address_test_db_1", + } + db.add(_idx_personal_info_1) + + # Prepare data: Token + token_address_1 = await deploy_share_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract_address, + require_personal_info_registered=True, + ) + _token_1 = Token() + _token_1.type = TokenType.IBET_SHARE.value + _token_1.tx_hash = "" + _token_1.issuer_address = issuer_address + _token_1.token_address = token_address_1 + _token_1.abi = {} + _token_1.version = TokenVersion.V_24_06 + db.add(_token_1) + + # Prepare data: UTXO + # - user_1: "2022/01/01" = 100 + 10, "2022/01/02" = 30 + 40 + # - user_2: "2022/01/01" = 200 + 20, "2022/01/02" = 40 + 2 + # - issuer: "2022/01/01" = 300 + _utxo_1 = UTXO() + _utxo_1.transaction_hash = "tx1" + _utxo_1.account_address = user_address_1 + _utxo_1.token_address = token_address_1 + _utxo_1.amount = 100 + _utxo_1.block_number = 1 + _utxo_1.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_1) + + _utxo_2 = UTXO() + _utxo_2.transaction_hash = "tx2" + _utxo_2.account_address = user_address_1 + _utxo_2.token_address = token_address_1 + _utxo_2.amount = 10 + _utxo_2.block_number = 2 + _utxo_2.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_2) + + _utxo_3 = UTXO() + _utxo_3.transaction_hash = "tx3" + _utxo_3.account_address = user_address_1 + _utxo_3.token_address = token_address_1 + _utxo_3.amount = 30 + _utxo_3.block_number = 3 + _utxo_3.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_3) + + _utxo_4 = UTXO() + _utxo_4.transaction_hash = "tx4" + _utxo_4.account_address = user_address_1 + _utxo_4.token_address = token_address_1 + _utxo_4.amount = 40 + _utxo_4.block_number = 4 + _utxo_4.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_4) + + _utxo_5 = UTXO() + _utxo_5.transaction_hash = "tx5" + _utxo_5.account_address = user_address_2 + _utxo_5.token_address = token_address_1 + _utxo_5.amount = 200 + _utxo_5.block_number = 5 + _utxo_5.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_5) + + _utxo_6 = UTXO() + _utxo_6.transaction_hash = "tx6" + _utxo_6.account_address = user_address_2 + _utxo_6.token_address = token_address_1 + _utxo_6.amount = 20 + _utxo_6.block_number = 6 + _utxo_6.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_6) + + _utxo_7 = UTXO() + _utxo_7.transaction_hash = "tx7" + _utxo_7.account_address = user_address_2 + _utxo_7.token_address = token_address_1 + _utxo_7.amount = 40 + _utxo_7.block_number = 7 + _utxo_7.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_7) + + _utxo_8 = UTXO() + _utxo_8.transaction_hash = "tx8" + _utxo_8.account_address = user_address_2 + _utxo_8.token_address = token_address_1 + _utxo_8.amount = 2 + _utxo_8.block_number = 8 + _utxo_8.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_8) + + _utxo_9 = UTXO() + _utxo_9.transaction_hash = "tx9" + _utxo_9.account_address = issuer_address + _utxo_9.token_address = token_address_1 + _utxo_9.amount = 300 + _utxo_9.block_number = 9 + _utxo_9.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_9) + + # Prepare data: Ledger template + _template = LedgerTemplate() + _template.token_address = token_address_1 + _template.issuer_address = issuer_address + _template.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ] + _template.token_name = "受益権テスト" + _template.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ] + db.add(_template) + + # Prepare data: Ledger template details 1 + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address_1 + _details_1.token_detail_type = "優先受益権" + _details_1.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": {"test-itemA": {"test-item": "test-value2Aa"}}, + }, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address_1 + db.add(_details_1) + + # Prepare data: Ledger template details 2 + _details_2 = LedgerDetailsTemplate() + _details_2.token_address = token_address_1 + _details_2.token_detail_type = "劣後受益権" + _details_2.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "d-test項目1": "d-test値1", + "d-test項目2": "d-test値2", + }, + ] + _details_2.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-d-test項目1": "d-test値1", + "f-d-test項目2": "d-test値2", + }, + ] + _details_2.data_type = LedgerDetailsDataType.DB.value + _details_2.data_source = "data_id_2" + db.add(_details_2) + + _details_2_data_1 = LedgerDetailsData() + _details_2_data_1.token_address = token_address_1 + _details_2_data_1.data_id = "data_id_2" + _details_2_data_1.name = "test_data_name_1" + _details_2_data_1.address = "test_data_address_1" + _details_2_data_1.amount = 100 + _details_2_data_1.price = 200 + _details_2_data_1.balance = 20000 + _details_2_data_1.acquisition_date = "2022/03/03" + db.add(_details_2_data_1) + + _details_2_data_2 = LedgerDetailsData() + _details_2_data_2.token_address = token_address_1 + _details_2_data_2.data_id = "data_id_2" + _details_2_data_2.name = "test_data_name_2" + _details_2_data_2.address = "test_data_address_2" + _details_2_data_2.amount = 30 + _details_2_data_2.price = 40 + _details_2_data_2.balance = 1200 + _details_2_data_2.acquisition_date = "2022/12/03" + db.add(_details_2_data_2) + + db.commit() + + # Execute + await ledger_utils.create_ledger(token_address_1, async_db) + await async_db.commit() + await async_db.close() + + # Assertion + _notifications = db.scalars(select(Notification)).all() + assert len(_notifications) == 1 + _notification = db.scalars(select(Notification).limit(1)).first() + assert _notification.id == 1 + assert _notification.notice_id is not None + assert _notification.issuer_address == issuer_address + assert _notification.priority == 0 + assert _notification.type == NotificationType.CREATE_LEDGER_INFO + assert _notification.code == 0 + assert _notification.metainfo == { + "token_address": token_address_1, + "token_type": TokenType.IBET_SHARE.value, + "ledger_id": 1, + } + + _ledger = db.scalars(select(Ledger).limit(1)).first() + assert _ledger.id == 1 + assert _ledger.token_address == token_address_1 + assert _ledger.token_type == TokenType.IBET_SHARE.value + now_ymd = datetime.now(pytz.timezone(TZ)).strftime("%Y/%m/%d") + assert _ledger.ledger == { + "created": now_ymd, + "token_name": "受益権テスト", + "currency": "", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ], + "details": [ + { + "token_detail_type": "優先受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ], + "data": [ + { + "account_address": issuer_address, + "name": None, + "address": None, + "amount": 300, + "price": 200, + "balance": 300 * 200, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_2, + "name": "name_test_con_2", + "address": "address_test_con_2", + "amount": 220, + "price": 200, + "balance": 220 * 200, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_2, + "name": "name_test_con_2", + "address": "address_test_con_2", + "amount": 42, + "price": 200, + "balance": 42 * 200, + "acquisition_date": "2022/01/02", + }, + { + "account_address": user_address_1, + "name": "name_test_db_1", + "address": "address_test_db_1", + "amount": 110, + "price": 200, + "balance": 110 * 200, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_1, + "name": "name_test_db_1", + "address": "address_test_db_1", + "amount": 70, + "price": 200, + "balance": 70 * 200, + "acquisition_date": "2022/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": { + "test-itemA": {"test-item": "test-value2Aa"} + }, + }, + ], + "some_personal_info_not_registered": False, + }, + { + "token_detail_type": "劣後受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "d-test項目1": "d-test値1", + "d-test項目2": "d-test値2", + }, + ], + "data": [ + { + "account_address": None, + "name": "test_data_name_1", + "address": "test_data_address_1", + "amount": 100, + "price": 200, + "balance": 20000, + "acquisition_date": "2022/03/03", + }, + { + "account_address": None, + "name": "test_data_name_2", + "address": "test_data_address_2", + "amount": 30, + "price": 40, + "balance": 1200, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-d-test項目1": "d-test値1", + "f-d-test項目2": "d-test値2", + }, + ], + "some_personal_info_not_registered": False, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ], + } + db.close() + + # + # Share Token + # require_personal_info_registered: True + # Some personal information except issuer one has not been registered + # -> "some_personal_info_not_registered" = True + @pytest.mark.asyncio + async def test_normal_1_1_2(self, db, async_db): + issuer = config_eth_account("user5") + issuer_address = issuer["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") + ) + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_2 = user_2["address"] + user_private_key_2 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data: Issuer account settings + _account = Account() + _account.issuer_address = issuer_address + _account.keyfile = issuer["keyfile_json"] + _account.eoa_password = E2EEUtils.encrypt("password") + _account.rsa_private_key = issuer["rsa_private_key"] + _account.rsa_public_key = issuer["rsa_public_key"] + _account.rsa_passphrase = E2EEUtils.encrypt("password") + _account.rsa_status = AccountRsaStatus.SET.value + db.add(_account) + + # Prepare data: Personal info contract + # - Only user_1 information is registered but the all field is null + personal_info_contract_address = deploy_personal_info_contract( + issuer_address, issuer_private_key + ) + await register_personal_info( + personal_info_contract_address, + _account, + [ + { + "address": user_address_1, + "private_key": user_private_key_1, + "data": { + "name": None, + "address": None, + }, + }, + ], + ) + + # There is no indexed personal info + + # Prepare data: Token + token_address_1 = await deploy_share_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract_address, + require_personal_info_registered=True, + ) + _token_1 = Token() + _token_1.type = TokenType.IBET_SHARE.value + _token_1.tx_hash = "" + _token_1.issuer_address = issuer_address + _token_1.token_address = token_address_1 + _token_1.abi = {} + _token_1.version = TokenVersion.V_24_06 + db.add(_token_1) + + # Prepare data: UTXO + # - user_1: "2022/01/01" = 100 + 10, "2022/01/02" = 30 + 40 + # - user_2: "2022/01/01" = 200 + 20, "2022/01/02" = 40 + 2 + # - issuer: "2022/01/01" = 300 + _utxo_1 = UTXO() + _utxo_1.transaction_hash = "tx1" + _utxo_1.account_address = user_address_1 + _utxo_1.token_address = token_address_1 + _utxo_1.amount = 100 + _utxo_1.block_number = 1 + _utxo_1.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_1) + + _utxo_2 = UTXO() + _utxo_2.transaction_hash = "tx2" + _utxo_2.account_address = user_address_1 + _utxo_2.token_address = token_address_1 + _utxo_2.amount = 10 + _utxo_2.block_number = 2 + _utxo_2.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_2) + + _utxo_3 = UTXO() + _utxo_3.transaction_hash = "tx3" + _utxo_3.account_address = user_address_1 + _utxo_3.token_address = token_address_1 + _utxo_3.amount = 30 + _utxo_3.block_number = 3 + _utxo_3.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_3) + + _utxo_4 = UTXO() + _utxo_4.transaction_hash = "tx4" + _utxo_4.account_address = user_address_1 + _utxo_4.token_address = token_address_1 + _utxo_4.amount = 40 + _utxo_4.block_number = 4 + _utxo_4.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_4) + + _utxo_5 = UTXO() + _utxo_5.transaction_hash = "tx5" + _utxo_5.account_address = user_address_2 + _utxo_5.token_address = token_address_1 + _utxo_5.amount = 200 + _utxo_5.block_number = 5 + _utxo_5.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_5) + + _utxo_6 = UTXO() + _utxo_6.transaction_hash = "tx6" + _utxo_6.account_address = user_address_2 + _utxo_6.token_address = token_address_1 + _utxo_6.amount = 20 + _utxo_6.block_number = 6 + _utxo_6.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_6) + + _utxo_7 = UTXO() + _utxo_7.transaction_hash = "tx7" + _utxo_7.account_address = user_address_2 + _utxo_7.token_address = token_address_1 + _utxo_7.amount = 40 + _utxo_7.block_number = 7 + _utxo_7.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_7) + + _utxo_8 = UTXO() + _utxo_8.transaction_hash = "tx8" + _utxo_8.account_address = user_address_2 + _utxo_8.token_address = token_address_1 + _utxo_8.amount = 2 + _utxo_8.block_number = 8 + _utxo_8.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_8) + + _utxo_9 = UTXO() + _utxo_9.transaction_hash = "tx9" + _utxo_9.account_address = issuer_address + _utxo_9.token_address = token_address_1 + _utxo_9.amount = 300 + _utxo_9.block_number = 9 + _utxo_9.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_9) + + # Prepare data: Ledger template + _template = LedgerTemplate() + _template.token_address = token_address_1 + _template.issuer_address = issuer_address + _template.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ] + _template.token_name = "受益権テスト" + _template.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ] + db.add(_template) + + # Prepare data: Ledger template details 1 + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address_1 + _details_1.token_detail_type = "優先受益権" + _details_1.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": {"test-itemA": {"test-item": "test-value2Aa"}}, + }, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address_1 + db.add(_details_1) + + # Prepare data: Ledger template details 2 + _details_2 = LedgerDetailsTemplate() + _details_2.token_address = token_address_1 + _details_2.token_detail_type = "劣後受益権" + _details_2.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "d-test項目1": "d-test値1", + "d-test項目2": "d-test値2", + }, + ] + _details_2.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-d-test項目1": "d-test値1", + "f-d-test項目2": "d-test値2", + }, + ] + _details_2.data_type = LedgerDetailsDataType.DB.value + _details_2.data_source = "data_id_2" + db.add(_details_2) + + _details_2_data_1 = LedgerDetailsData() + _details_2_data_1.token_address = token_address_1 + _details_2_data_1.data_id = "data_id_2" + _details_2_data_1.name = "test_data_name_1" + _details_2_data_1.address = "test_data_address_1" + _details_2_data_1.amount = 100 + _details_2_data_1.price = 200 + _details_2_data_1.balance = 20000 + _details_2_data_1.acquisition_date = "2022/03/03" + db.add(_details_2_data_1) + + _details_2_data_2 = LedgerDetailsData() + _details_2_data_2.token_address = token_address_1 + _details_2_data_2.data_id = "data_id_2" + _details_2_data_2.name = "test_data_name_2" + _details_2_data_2.address = "test_data_address_2" + _details_2_data_2.amount = 30 + _details_2_data_2.price = 40 + _details_2_data_2.balance = 1200 + _details_2_data_2.acquisition_date = "2022/12/03" + db.add(_details_2_data_2) + + db.commit() + + # Execute + await ledger_utils.create_ledger(token_address_1, async_db) + await async_db.commit() + await async_db.close() + + # Assertion + _notifications = db.scalars(select(Notification)).all() + assert len(_notifications) == 1 + _notification = db.scalars(select(Notification).limit(1)).first() + assert _notification.id == 1 + assert _notification.notice_id is not None + assert _notification.issuer_address == issuer_address + assert _notification.priority == 0 + assert _notification.type == NotificationType.CREATE_LEDGER_INFO + assert _notification.code == 0 + assert _notification.metainfo == { + "token_address": token_address_1, + "token_type": TokenType.IBET_SHARE.value, + "ledger_id": 1, + } + + _ledger = db.scalars(select(Ledger).limit(1)).first() + assert _ledger.id == 1 + assert _ledger.token_address == token_address_1 + assert _ledger.token_type == TokenType.IBET_SHARE.value + now_ymd = datetime.now(pytz.timezone(TZ)).strftime("%Y/%m/%d") + assert _ledger.ledger == { + "created": now_ymd, + "token_name": "受益権テスト", + "currency": "", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ], + "details": [ + { + "token_detail_type": "優先受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ], + "data": [ + { + "account_address": issuer_address, + "name": None, + "address": None, + "amount": 300, + "price": 200, + "balance": 300 * 200, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_2, + "name": None, + "address": None, + "amount": 220, + "price": 200, + "balance": 220 * 200, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_2, + "name": None, + "address": None, + "amount": 42, + "price": 200, + "balance": 42 * 200, + "acquisition_date": "2022/01/02", + }, + { + "account_address": user_address_1, + "name": None, + "address": None, + "amount": 110, + "price": 200, + "balance": 110 * 200, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_1, + "name": None, + "address": None, + "amount": 70, + "price": 200, + "balance": 70 * 200, + "acquisition_date": "2022/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": { + "test-itemA": {"test-item": "test-value2Aa"} + }, + }, + ], + "some_personal_info_not_registered": True, + }, + { + "token_detail_type": "劣後受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "d-test項目1": "d-test値1", + "d-test項目2": "d-test値2", + }, + ], + "data": [ + { + "account_address": None, + "name": "test_data_name_1", + "address": "test_data_address_1", + "amount": 100, + "price": 200, + "balance": 20000, + "acquisition_date": "2022/03/03", + }, + { + "account_address": None, + "name": "test_data_name_2", + "address": "test_data_address_2", + "amount": 30, + "price": 40, + "balance": 1200, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-d-test項目1": "d-test値1", + "f-d-test項目2": "d-test値2", + }, + ], + "some_personal_info_not_registered": False, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ], + } + db.close() + + # + # Share Token + # require_personal_info_registered: False + # - Perform searches only on indexed personal information. + # - Even if personal information exists in the contract, it will not be referenced. + # No personal information indexed + # -> "some_personal_info_not_registered" = True + @pytest.mark.asyncio + async def test_normal_1_2(self, db, async_db): + issuer = config_eth_account("user5") + issuer_address = issuer["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") + ) + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data: Issuer account settings + _account = Account() + _account.issuer_address = issuer_address + _account.keyfile = issuer["keyfile_json"] + _account.eoa_password = E2EEUtils.encrypt("password") + _account.rsa_private_key = issuer["rsa_private_key"] + _account.rsa_public_key = issuer["rsa_public_key"] + _account.rsa_passphrase = E2EEUtils.encrypt("password") + _account.rsa_status = AccountRsaStatus.SET.value + db.add(_account) + + # Prepare data: Personal info contract + # - Registered data is not referenced + personal_info_contract_address = deploy_personal_info_contract( + issuer_address, issuer_private_key + ) + await register_personal_info( + personal_info_contract_address, + _account, + [ + { + "address": user_address_1, + "private_key": user_private_key_1, + "data": { + "name": "name_test_con_1", + "address": "address_test_con_1", + }, + }, + ], + ) + + # Prepare data: Token + token_address_1 = await deploy_share_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract_address, + require_personal_info_registered=False, + ) + _token_1 = Token() + _token_1.type = TokenType.IBET_SHARE.value + _token_1.tx_hash = "" + _token_1.issuer_address = issuer_address + _token_1.token_address = token_address_1 + _token_1.abi = {} + _token_1.version = TokenVersion.V_24_06 + db.add(_token_1) + + # Prepare data: UTXO + # - user_1: "2022/01/01" = 100 + 10, "2022/01/02" = 30 + 40 + _utxo_1 = UTXO() + _utxo_1.transaction_hash = "tx1" + _utxo_1.account_address = user_address_1 + _utxo_1.token_address = token_address_1 + _utxo_1.amount = 100 + _utxo_1.block_number = 1 + _utxo_1.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_1) + + _utxo_2 = UTXO() + _utxo_2.transaction_hash = "tx2" + _utxo_2.account_address = user_address_1 + _utxo_2.token_address = token_address_1 + _utxo_2.amount = 10 + _utxo_2.block_number = 2 + _utxo_2.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_2) + + _utxo_3 = UTXO() + _utxo_3.transaction_hash = "tx3" + _utxo_3.account_address = user_address_1 + _utxo_3.token_address = token_address_1 + _utxo_3.amount = 30 + _utxo_3.block_number = 3 + _utxo_3.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_3) + + _utxo_4 = UTXO() + _utxo_4.transaction_hash = "tx4" + _utxo_4.account_address = user_address_1 + _utxo_4.token_address = token_address_1 + _utxo_4.amount = 40 + _utxo_4.block_number = 4 + _utxo_4.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_4) + + # Prepare data: Ledger template + _template = LedgerTemplate() + _template.token_address = token_address_1 + _template.issuer_address = issuer_address + _template.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ] + _template.token_name = "受益権テスト" + _template.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ] + db.add(_template) + + # Prepare data: Ledger template details 1 + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address_1 + _details_1.token_detail_type = "優先受益権" + _details_1.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": {"test-itemA": {"test-item": "test-value2Aa"}}, + }, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address_1 + db.add(_details_1) + + db.commit() + + # Execute + await ledger_utils.create_ledger(token_address_1, async_db) + await async_db.commit() + await async_db.close() + + # Assertion + _notifications = db.scalars(select(Notification)).all() + assert len(_notifications) == 1 + + _notification = db.scalars(select(Notification).limit(1)).first() + assert _notification.id == 1 + assert _notification.notice_id is not None + assert _notification.issuer_address == issuer_address + assert _notification.priority == 0 + assert _notification.type == NotificationType.CREATE_LEDGER_INFO + assert _notification.code == 0 + assert _notification.metainfo == { + "token_address": token_address_1, + "token_type": TokenType.IBET_SHARE.value, + "ledger_id": 1, + } + + _ledger = db.scalars(select(Ledger).limit(1)).first() + assert _ledger.id == 1 + assert _ledger.token_address == token_address_1 + assert _ledger.token_type == TokenType.IBET_SHARE.value + now_ymd = datetime.now(pytz.timezone(TZ)).strftime("%Y/%m/%d") + assert _ledger.ledger == { + "created": now_ymd, + "token_name": "受益権テスト", + "currency": "", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ], + "details": [ + { + "token_detail_type": "優先受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ], + "data": [ + { + "account_address": user_address_1, + "name": None, # Not set + "address": None, # Not set + "amount": 110, + "price": 200, + "balance": 110 * 200, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_1, + "name": None, # Not set + "address": None, # Not set + "amount": 70, + "price": 200, + "balance": 70 * 200, + "acquisition_date": "2022/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": { + "test-itemA": {"test-item": "test-value2Aa"} + }, + }, + ], + "some_personal_info_not_registered": True, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ], + } + db.close() + + # + # Bond Token + # require_personal_info_registered: True + # All personal information except issuer one has been registered + # -> "some_personal_info_not_registered" = False + @pytest.mark.asyncio + async def test_normal_2_1_1(self, db, async_db): + issuer = config_eth_account("user5") + issuer_address = issuer["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") + ) + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_2 = user_2["address"] + user_private_key_2 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data: Issuer account settings + _account = Account() + _account.issuer_address = issuer_address + _account.keyfile = issuer["keyfile_json"] + _account.eoa_password = E2EEUtils.encrypt("password") + _account.rsa_private_key = issuer["rsa_private_key"] + _account.rsa_public_key = issuer["rsa_public_key"] + _account.rsa_passphrase = E2EEUtils.encrypt("password") + _account.rsa_status = AccountRsaStatus.SET.value + db.add(_account) + + # Prepare data: Personal info contract + personal_info_contract_address = deploy_personal_info_contract( + issuer_address, issuer_private_key + ) + await register_personal_info( + personal_info_contract_address, + _account, + [ + { + "address": user_address_1, + "private_key": user_private_key_1, + "data": { + "name": "name_test_con_1", + "address": "address_test_con_1", + }, + }, + { + "address": user_address_2, + "private_key": user_private_key_2, + "data": { + "name": "name_test_con_2", + "address": "address_test_con_2", + }, + }, + ], + ) + + # Prepare data: IDXPersonalInfo + # - Only user_1 + # - user_2 information is obtained from contract data + _idx_personal_info_1 = IDXPersonalInfo() + _idx_personal_info_1.account_address = user_address_1 + _idx_personal_info_1.issuer_address = issuer_address + _idx_personal_info_1.personal_info = { + "name": "name_test_db_1", + "address": "address_test_db_1", + } + db.add(_idx_personal_info_1) + + # Prepare data: Token + token_address_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract_address, + require_personal_info_registered=True, + ) + _token_1 = Token() + _token_1.type = TokenType.IBET_STRAIGHT_BOND.value + _token_1.tx_hash = "" + _token_1.issuer_address = issuer_address + _token_1.token_address = token_address_1 + _token_1.abi = {} + _token_1.version = TokenVersion.V_24_06 + db.add(_token_1) + + # Prepare data: UTXO + # - user_1: "2022/01/01" = 100 + 10, "2022/01/02" = 30 + 40 + # - user_2: "2022/01/01" = 200 + 20, "2022/01/02" = 40 + 2 + _utxo_1 = UTXO() + _utxo_1.transaction_hash = "tx1" + _utxo_1.account_address = user_address_1 + _utxo_1.token_address = token_address_1 + _utxo_1.amount = 100 + _utxo_1.block_number = 1 + _utxo_1.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_1) + + _utxo_2 = UTXO() + _utxo_2.transaction_hash = "tx2" + _utxo_2.account_address = user_address_1 + _utxo_2.token_address = token_address_1 + _utxo_2.amount = 10 + _utxo_2.block_number = 2 + _utxo_2.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_2) + + _utxo_3 = UTXO() + _utxo_3.transaction_hash = "tx3" + _utxo_3.account_address = user_address_1 + _utxo_3.token_address = token_address_1 + _utxo_3.amount = 30 + _utxo_3.block_number = 3 + _utxo_3.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_3) + + _utxo_4 = UTXO() + _utxo_4.transaction_hash = "tx4" + _utxo_4.account_address = user_address_1 + _utxo_4.token_address = token_address_1 + _utxo_4.amount = 40 + _utxo_4.block_number = 4 + _utxo_4.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_4) + + _utxo_5 = UTXO() + _utxo_5.transaction_hash = "tx5" + _utxo_5.account_address = user_address_2 + _utxo_5.token_address = token_address_1 + _utxo_5.amount = 200 + _utxo_5.block_number = 5 + _utxo_5.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_5) + + _utxo_6 = UTXO() + _utxo_6.transaction_hash = "tx6" + _utxo_6.account_address = user_address_2 + _utxo_6.token_address = token_address_1 + _utxo_6.amount = 20 + _utxo_6.block_number = 6 + _utxo_6.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_6) + + _utxo_7 = UTXO() + _utxo_7.transaction_hash = "tx7" + _utxo_7.account_address = user_address_2 + _utxo_7.token_address = token_address_1 + _utxo_7.amount = 40 + _utxo_7.block_number = 7 + _utxo_7.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_7) + + _utxo_8 = UTXO() + _utxo_8.transaction_hash = "tx8" + _utxo_8.account_address = user_address_2 + _utxo_8.token_address = token_address_1 + _utxo_8.amount = 2 + _utxo_8.block_number = 8 + _utxo_8.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_8) + + _utxo_9 = UTXO() + _utxo_9.transaction_hash = "tx9" + _utxo_9.account_address = issuer_address + _utxo_9.token_address = token_address_1 + _utxo_9.amount = 300 + _utxo_9.block_number = 9 + _utxo_9.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_9) + + # Prepare data: Ledger template + _template = LedgerTemplate() + _template.token_address = token_address_1 + _template.issuer_address = issuer_address + _template.token_name = "受益権テスト" + _template.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ] + _template.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ] + db.add(_template) + + # Prepare data: Ledger template details 1 + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address_1 + _details_1.token_detail_type = "優先受益権" + _details_1.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": {"test-itemA": {"test-item": "test-value2Aa"}}, + }, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address_1 + db.add(_details_1) + + # Prepare data: Ledger template details 2 + _details_2 = LedgerDetailsTemplate() + _details_2.token_address = token_address_1 + _details_2.token_detail_type = "劣後受益権" + _details_2.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "d-test項目1": "d-test値1", + "d-test項目2": "d-test値2", + }, + ] + _details_2.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-d-test項目1": "d-test値1", + "f-d-test項目2": "d-test値2", + }, + ] + _details_2.data_type = LedgerDetailsDataType.DB.value + _details_2.data_source = "data_id_2" + db.add(_details_2) + + _details_2_data_1 = LedgerDetailsData() + _details_2_data_1.token_address = token_address_1 + _details_2_data_1.data_id = "data_id_2" + _details_2_data_1.name = "test_data_name_1" + _details_2_data_1.address = "test_data_address_1" + _details_2_data_1.amount = 100 + _details_2_data_1.price = 200 + _details_2_data_1.balance = 20000 + _details_2_data_1.acquisition_date = "2022/03/03" + db.add(_details_2_data_1) + + _details_2_data_2 = LedgerDetailsData() + _details_2_data_2.token_address = token_address_1 + _details_2_data_2.data_id = "data_id_2" + _details_2_data_2.name = "test_data_name_2" + _details_2_data_2.address = "test_data_address_2" + _details_2_data_2.amount = 30 + _details_2_data_2.price = 40 + _details_2_data_2.balance = 1200 + _details_2_data_2.acquisition_date = "2022/12/03" + db.add(_details_2_data_2) + + db.commit() + + # Execute + await ledger_utils.create_ledger(token_address_1, async_db) + await async_db.commit() + await async_db.close() + + # Assertion + _notifications = db.scalars(select(Notification)).all() + assert len(_notifications) == 1 + _notification = db.scalars(select(Notification).limit(1)).first() + assert _notification.id == 1 + assert _notification.notice_id is not None + assert _notification.issuer_address == issuer_address + assert _notification.priority == 0 + assert _notification.type == NotificationType.CREATE_LEDGER_INFO + assert _notification.code == 0 + assert _notification.metainfo == { + "token_address": token_address_1, + "token_type": TokenType.IBET_STRAIGHT_BOND.value, + "ledger_id": 1, + } + _ledger = db.scalars(select(Ledger).limit(1)).first() + assert _ledger.id == 1 + assert _ledger.token_address == token_address_1 + assert _ledger.token_type == TokenType.IBET_STRAIGHT_BOND.value + now_ymd = datetime.now(pytz.timezone(TZ)).strftime("%Y/%m/%d") + assert _ledger.ledger == { + "created": now_ymd, + "token_name": "受益権テスト", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ], + "details": [ + { + "token_detail_type": "優先受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ], + "data": [ + { + "account_address": issuer_address, + "name": None, + "address": None, + "amount": 300, + "price": 20, + "balance": 300 * 20, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_2, + "name": "name_test_con_2", + "address": "address_test_con_2", + "amount": 220, + "price": 20, + "balance": 220 * 20, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_2, + "name": "name_test_con_2", + "address": "address_test_con_2", + "amount": 42, + "price": 20, + "balance": 42 * 20, + "acquisition_date": "2022/01/02", + }, + { + "account_address": user_address_1, + "name": "name_test_db_1", + "address": "address_test_db_1", + "amount": 110, + "price": 20, + "balance": 110 * 20, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_1, + "name": "name_test_db_1", + "address": "address_test_db_1", + "amount": 70, + "price": 20, + "balance": 70 * 20, + "acquisition_date": "2022/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": { + "test-itemA": {"test-item": "test-value2Aa"} + }, + }, + ], + "some_personal_info_not_registered": False, + }, + { + "token_detail_type": "劣後受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "d-test項目1": "d-test値1", + "d-test項目2": "d-test値2", + }, + ], + "data": [ + { + "account_address": None, + "name": "test_data_name_1", + "address": "test_data_address_1", + "amount": 100, + "price": 200, + "balance": 20000, + "acquisition_date": "2022/03/03", + }, + { + "account_address": None, + "name": "test_data_name_2", + "address": "test_data_address_2", + "amount": 30, + "price": 40, + "balance": 1200, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-d-test項目1": "d-test値1", + "f-d-test項目2": "d-test値2", + }, + ], + "some_personal_info_not_registered": False, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ], + } + db.close() + + # + # Bond Token + # require_personal_info_registered: True + # Some personal information except issuer one has not been registered + # -> "some_personal_info_not_registered" = True + @pytest.mark.asyncio + async def test_normal_2_1_2(self, db, async_db): + issuer = config_eth_account("user5") + issuer_address = issuer["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") + ) + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_2 = user_2["address"] + user_private_key_2 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data: Issuer account settings + _account = Account() + _account.issuer_address = issuer_address + _account.keyfile = issuer["keyfile_json"] + _account.eoa_password = E2EEUtils.encrypt("password") + _account.rsa_private_key = issuer["rsa_private_key"] + _account.rsa_public_key = issuer["rsa_public_key"] + _account.rsa_passphrase = E2EEUtils.encrypt("password") + _account.rsa_status = AccountRsaStatus.SET.value + db.add(_account) + + # Prepare data: Personal info contract + # - Only user_1 information is registered but the all field is null + personal_info_contract_address = deploy_personal_info_contract( + issuer_address, issuer_private_key + ) + await register_personal_info( + personal_info_contract_address, + _account, + [ + { + "address": user_address_1, + "private_key": user_private_key_1, + "data": { + "name": None, + "address": None, + }, + }, + ], + ) + + # There is no indexed personal info + + # Prepare data: Token + token_address_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract_address, + require_personal_info_registered=True, + ) + _token_1 = Token() + _token_1.type = TokenType.IBET_STRAIGHT_BOND.value + _token_1.tx_hash = "" + _token_1.issuer_address = issuer_address + _token_1.token_address = token_address_1 + _token_1.abi = {} + _token_1.version = TokenVersion.V_24_06 + db.add(_token_1) + + # Prepare data: UTXO + # - user_1: "2022/01/01" = 100 + 10, "2022/01/02" = 30 + 40 + # - user_2: "2022/01/01" = 200 + 20, "2022/01/02" = 40 + 2 + _utxo_1 = UTXO() + _utxo_1.transaction_hash = "tx1" + _utxo_1.account_address = user_address_1 + _utxo_1.token_address = token_address_1 + _utxo_1.amount = 100 + _utxo_1.block_number = 1 + _utxo_1.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_1) + + _utxo_2 = UTXO() + _utxo_2.transaction_hash = "tx2" + _utxo_2.account_address = user_address_1 + _utxo_2.token_address = token_address_1 + _utxo_2.amount = 10 + _utxo_2.block_number = 2 + _utxo_2.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_2) + + _utxo_3 = UTXO() + _utxo_3.transaction_hash = "tx3" + _utxo_3.account_address = user_address_1 + _utxo_3.token_address = token_address_1 + _utxo_3.amount = 30 + _utxo_3.block_number = 3 + _utxo_3.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_3) + + _utxo_4 = UTXO() + _utxo_4.transaction_hash = "tx4" + _utxo_4.account_address = user_address_1 + _utxo_4.token_address = token_address_1 + _utxo_4.amount = 40 + _utxo_4.block_number = 4 + _utxo_4.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_4) + + _utxo_5 = UTXO() + _utxo_5.transaction_hash = "tx5" + _utxo_5.account_address = user_address_2 + _utxo_5.token_address = token_address_1 + _utxo_5.amount = 200 + _utxo_5.block_number = 5 + _utxo_5.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_5) + + _utxo_6 = UTXO() + _utxo_6.transaction_hash = "tx6" + _utxo_6.account_address = user_address_2 + _utxo_6.token_address = token_address_1 + _utxo_6.amount = 20 + _utxo_6.block_number = 6 + _utxo_6.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_6) + + _utxo_7 = UTXO() + _utxo_7.transaction_hash = "tx7" + _utxo_7.account_address = user_address_2 + _utxo_7.token_address = token_address_1 + _utxo_7.amount = 40 + _utxo_7.block_number = 7 + _utxo_7.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_7) + + _utxo_8 = UTXO() + _utxo_8.transaction_hash = "tx8" + _utxo_8.account_address = user_address_2 + _utxo_8.token_address = token_address_1 + _utxo_8.amount = 2 + _utxo_8.block_number = 8 + _utxo_8.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_8) + + _utxo_9 = UTXO() + _utxo_9.transaction_hash = "tx9" + _utxo_9.account_address = issuer_address + _utxo_9.token_address = token_address_1 + _utxo_9.amount = 300 + _utxo_9.block_number = 9 + _utxo_9.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_9) + + # Prepare data: Ledger template + _template = LedgerTemplate() + _template.token_address = token_address_1 + _template.issuer_address = issuer_address + _template.token_name = "受益権テスト" + _template.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ] + _template.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ] + db.add(_template) + + # Prepare data: Ledger template details 1 + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address_1 + _details_1.token_detail_type = "優先受益権" + _details_1.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": {"test-itemA": {"test-item": "test-value2Aa"}}, + }, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address_1 + db.add(_details_1) + + # Prepare data: Ledger template details 2 + _details_2 = LedgerDetailsTemplate() + _details_2.token_address = token_address_1 + _details_2.token_detail_type = "劣後受益権" + _details_2.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "d-test項目1": "d-test値1", + "d-test項目2": "d-test値2", + }, + ] + _details_2.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-d-test項目1": "d-test値1", + "f-d-test項目2": "d-test値2", + }, + ] + _details_2.data_type = LedgerDetailsDataType.DB.value + _details_2.data_source = "data_id_2" + db.add(_details_2) + + _details_2_data_1 = LedgerDetailsData() + _details_2_data_1.token_address = token_address_1 + _details_2_data_1.data_id = "data_id_2" + _details_2_data_1.name = "test_data_name_1" + _details_2_data_1.address = "test_data_address_1" + _details_2_data_1.amount = 100 + _details_2_data_1.price = 200 + _details_2_data_1.balance = 20000 + _details_2_data_1.acquisition_date = "2022/03/03" + db.add(_details_2_data_1) + + _details_2_data_2 = LedgerDetailsData() + _details_2_data_2.token_address = token_address_1 + _details_2_data_2.data_id = "data_id_2" + _details_2_data_2.name = "test_data_name_2" + _details_2_data_2.address = "test_data_address_2" + _details_2_data_2.amount = 30 + _details_2_data_2.price = 40 + _details_2_data_2.balance = 1200 + _details_2_data_2.acquisition_date = "2022/12/03" + db.add(_details_2_data_2) + + db.commit() + + # Execute + await ledger_utils.create_ledger(token_address_1, async_db) + await async_db.commit() + await async_db.close() + + # Assertion + _notifications = db.scalars(select(Notification)).all() + assert len(_notifications) == 1 + _notification = db.scalars(select(Notification).limit(1)).first() + assert _notification.id == 1 + assert _notification.notice_id is not None + assert _notification.issuer_address == issuer_address + assert _notification.priority == 0 + assert _notification.type == NotificationType.CREATE_LEDGER_INFO + assert _notification.code == 0 + assert _notification.metainfo == { + "token_address": token_address_1, + "token_type": TokenType.IBET_STRAIGHT_BOND.value, + "ledger_id": 1, + } + _ledger = db.scalars(select(Ledger).limit(1)).first() + assert _ledger.id == 1 + assert _ledger.token_address == token_address_1 + assert _ledger.token_type == TokenType.IBET_STRAIGHT_BOND.value + now_ymd = datetime.now(pytz.timezone(TZ)).strftime("%Y/%m/%d") + assert _ledger.ledger == { + "created": now_ymd, + "token_name": "受益権テスト", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ], + "details": [ + { + "token_detail_type": "優先受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ], + "data": [ + { + "account_address": issuer_address, + "name": None, + "address": None, + "amount": 300, + "price": 20, + "balance": 300 * 20, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_2, + "name": None, + "address": None, + "amount": 220, + "price": 20, + "balance": 220 * 20, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_2, + "name": None, + "address": None, + "amount": 42, + "price": 20, + "balance": 42 * 20, + "acquisition_date": "2022/01/02", + }, + { + "account_address": user_address_1, + "name": None, + "address": None, + "amount": 110, + "price": 20, + "balance": 110 * 20, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_1, + "name": None, + "address": None, + "amount": 70, + "price": 20, + "balance": 70 * 20, + "acquisition_date": "2022/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": { + "test-itemA": {"test-item": "test-value2Aa"} + }, + }, + ], + "some_personal_info_not_registered": True, + }, + { + "token_detail_type": "劣後受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "d-test項目1": "d-test値1", + "d-test項目2": "d-test値2", + }, + ], + "data": [ + { + "account_address": None, + "name": "test_data_name_1", + "address": "test_data_address_1", + "amount": 100, + "price": 200, + "balance": 20000, + "acquisition_date": "2022/03/03", + }, + { + "account_address": None, + "name": "test_data_name_2", + "address": "test_data_address_2", + "amount": 30, + "price": 40, + "balance": 1200, + "acquisition_date": "2022/12/03", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-d-test項目1": "d-test値1", + "f-d-test項目2": "d-test値2", + }, + ], + "some_personal_info_not_registered": False, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ], + } + db.close() + + # + # Bond Token + # require_personal_info_registered: False + # - Perform searches only on indexed personal information. + # - Even if personal information exists in the contract, it will not be referenced. + # No personal information indexed + # -> "some_personal_info_not_registered" = True + @pytest.mark.asyncio + async def test_normal_2_2(self, db, async_db): + issuer = config_eth_account("user5") + issuer_address = issuer["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") + ) + user_1 = config_eth_account("user1") + user_address_1 = user_1["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data: Issuer account settings + _account = Account() + _account.issuer_address = issuer_address + _account.keyfile = issuer["keyfile_json"] + _account.eoa_password = E2EEUtils.encrypt("password") + _account.rsa_private_key = issuer["rsa_private_key"] + _account.rsa_public_key = issuer["rsa_public_key"] + _account.rsa_passphrase = E2EEUtils.encrypt("password") + _account.rsa_status = AccountRsaStatus.SET.value + db.add(_account) + + # Prepare data: Personal info contract + # - Registered data is not referenced + personal_info_contract_address = deploy_personal_info_contract( + issuer_address, issuer_private_key + ) + await register_personal_info( + personal_info_contract_address, + _account, + [ + { + "address": user_address_1, + "private_key": user_private_key_1, + "data": { + "name": "name_test_con_1", + "address": "address_test_con_1", + }, + }, + ], + ) + + # Prepare data: Token + token_address_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract_address, + require_personal_info_registered=False, + ) + _token_1 = Token() + _token_1.type = TokenType.IBET_STRAIGHT_BOND.value + _token_1.tx_hash = "" + _token_1.issuer_address = issuer_address + _token_1.token_address = token_address_1 + _token_1.abi = {} + _token_1.version = TokenVersion.V_24_06 + db.add(_token_1) + + # Prepare data: UTXO + # - user_1: "2022/01/01" = 100 + 10, "2022/01/02" = 30 + 40 + _utxo_1 = UTXO() + _utxo_1.transaction_hash = "tx1" + _utxo_1.account_address = user_address_1 + _utxo_1.token_address = token_address_1 + _utxo_1.amount = 100 + _utxo_1.block_number = 1 + _utxo_1.block_timestamp = datetime.strptime( + "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_1) + + _utxo_2 = UTXO() + _utxo_2.transaction_hash = "tx2" + _utxo_2.account_address = user_address_1 + _utxo_2.token_address = token_address_1 + _utxo_2.amount = 10 + _utxo_2.block_number = 2 + _utxo_2.block_timestamp = datetime.strptime( + "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/01 + db.add(_utxo_2) + + _utxo_3 = UTXO() + _utxo_3.transaction_hash = "tx3" + _utxo_3.account_address = user_address_1 + _utxo_3.token_address = token_address_1 + _utxo_3.amount = 30 + _utxo_3.block_number = 3 + _utxo_3.block_timestamp = datetime.strptime( + "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_3) + + _utxo_4 = UTXO() + _utxo_4.transaction_hash = "tx4" + _utxo_4.account_address = user_address_1 + _utxo_4.token_address = token_address_1 + _utxo_4.amount = 40 + _utxo_4.block_number = 4 + _utxo_4.block_timestamp = datetime.strptime( + "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" + ) # JST 2022/01/02 + db.add(_utxo_4) + + # Prepare data: Ledger template + _template = LedgerTemplate() + _template.token_address = token_address_1 + _template.issuer_address = issuer_address + _template.token_name = "受益権テスト" + _template.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ] + _template.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ] + db.add(_template) + + # Prepare data: Ledger template details 1 + _details_1 = LedgerDetailsTemplate() + _details_1.token_address = token_address_1 + _details_1.token_detail_type = "優先受益権" + _details_1.headers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ] + _details_1.footers = [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": {"test-itemA": {"test-item": "test-value2Aa"}}, + }, + ] + _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value + _details_1.data_source = token_address_1 + db.add(_details_1) + + db.commit() + + # Execute + await ledger_utils.create_ledger(token_address_1, async_db) + await async_db.commit() + await async_db.close() + + # Assertion + _notifications = db.scalars(select(Notification)).all() + assert len(_notifications) == 1 + + _notification = db.scalars(select(Notification).limit(1)).first() + assert _notification.id == 1 + assert _notification.notice_id is not None + assert _notification.issuer_address == issuer_address + assert _notification.priority == 0 + assert _notification.type == NotificationType.CREATE_LEDGER_INFO + assert _notification.code == 0 + assert _notification.metainfo == { + "token_address": token_address_1, + "token_type": TokenType.IBET_STRAIGHT_BOND.value, + "ledger_id": 1, + } + + _ledger = db.scalars(select(Ledger).limit(1)).first() + assert _ledger.id == 1 + assert _ledger.token_address == token_address_1 + assert _ledger.token_type == TokenType.IBET_STRAIGHT_BOND.value + now_ymd = datetime.now(pytz.timezone(TZ)).strftime("%Y/%m/%d") + assert _ledger.ledger == { + "created": now_ymd, + "token_name": "受益権テスト", + "currency": "JPY", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "テスト項目1": "テスト値1", + "テスト項目2": { + "テスト項目A": "テスト値2A", + "テスト項目B": "テスト値2B", + }, + "テスト項目3": { + "テスト項目A": {"テスト項目a": "テスト値3Aa"}, + "テスト項目B": "テスト値3B", + }, + }, + ], + "details": [ + { + "token_detail_type": "優先受益権", + "headers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test項目1": "test値1", + "test項目2": { + "test項目A": "test値2A", + }, + }, + ], + "data": [ + { + "account_address": user_address_1, + "name": None, # Not set + "address": None, # Not set + "amount": 110, + "price": 20, + "balance": 110 * 20, + "acquisition_date": "2022/01/01", + }, + { + "account_address": user_address_1, + "name": None, # Not set + "address": None, # Not set + "amount": 70, + "price": 20, + "balance": 70 * 20, + "acquisition_date": "2022/01/02", + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "test-item1": "test-value1", + "test-item2": { + "test-itemA": {"test-item": "test-value2Aa"} + }, + }, + ], + "some_personal_info_not_registered": True, + }, + ], + "footers": [ + { + "key": "aaa", + "value": "bbb", + }, + { + "f-テスト項目1": "f-テスト値1", + "f-テスト項目2": { + "f-テスト項目A": "f-テスト値2A", + "f-テスト項目B": "f-テスト値2B", + }, + "f-テスト項目3": { + "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, + "f-テスト項目B": "f-テスト値3B", + }, + }, + ], + } + db.close() + + # + # Ledger template is not set + # -> Skip processing + @pytest.mark.asyncio + async def test_normal_3(self, db, async_db): + issuer = config_eth_account("user5") + issuer_address = issuer["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data: Token + token_address_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + ZERO_ADDRESS, + require_personal_info_registered=True, + ) + _token_1 = Token() + _token_1.type = TokenType.IBET_STRAIGHT_BOND.value + _token_1.tx_hash = "" + _token_1.issuer_address = issuer_address + _token_1.token_address = token_address_1 + _token_1.abi = {} + _token_1.version = TokenVersion.V_24_06 + db.add(_token_1) + db.commit() + + # Execute + await ledger_utils.create_ledger(token_address_1, async_db) + await async_db.commit() + await async_db.close() + + # Assertion + _notifications = db.scalars(select(Notification)).all() + assert len(_notifications) == 0 + _ledger = db.scalars(select(Ledger).limit(1)).first() + assert _ledger is None + db.close() + + # + # Not a token type to be processed + # -> Skip processing + @pytest.mark.asyncio + async def test_normal_4(self, db, async_db): + issuer = config_eth_account("user5") + issuer_address = issuer["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data: Token + token_address_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + ZERO_ADDRESS, + require_personal_info_registered=True, + ) + _token_1 = Token() + _token_1.type = "IbetCoupon" # Not subject to processing + _token_1.tx_hash = "" + _token_1.issuer_address = issuer_address + _token_1.token_address = token_address_1 + _token_1.abi = {} + _token_1.version = TokenVersion.V_24_06 + db.add(_token_1) + + db.commit() + + # Execute + await ledger_utils.create_ledger(token_address_1, async_db) + await async_db.commit() + await async_db.close() + + # assertion + _notifications = db.scalars(select(Notification)).all() + assert len(_notifications) == 0 + _ledger = db.scalars(select(Ledger).limit(1)).first() + assert _ledger is None + db.close() + + ########################################################################### + # Error Case + ########################################################################### diff --git a/tests/test_batch_indexer_block_tx_data.py b/tests/batch/test_indexer_block_tx_data.py similarity index 97% rename from tests/test_batch_indexer_block_tx_data.py rename to tests/batch/test_indexer_block_tx_data.py index ac4d8115..781aa0fe 100644 --- a/tests/test_batch_indexer_block_tx_data.py +++ b/tests/batch/test_indexer_block_tx_data.py @@ -27,7 +27,7 @@ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.ext.asyncio import AsyncSession from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from web3.types import RPCEndpoint from app.exceptions import ServiceUnavailableError @@ -36,10 +36,10 @@ from batch.indexer_block_tx_data import LOG from config import CHAIN_ID, WEB3_HTTP_PROVIDER, ZERO_ADDRESS from tests.account_config import config_eth_account -from tests.utils.contract_utils import IbetStandardTokenUtils +from tests.contract_utils import IbetStandardTokenUtils web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) @pytest.fixture(scope="function") @@ -272,7 +272,7 @@ async def test_error_1(self, processor, db): # Execute batch processing with mock.patch( - "web3.providers.async_rpc.AsyncHTTPProvider.make_request", + "web3.providers.rpc.async_rpc.AsyncHTTPProvider.make_request", MagicMock(side_effect=ServiceUnavailableError()), ), pytest.raises(ServiceUnavailableError): await processor.process() diff --git a/tests/batch/test_indexer_delivery.py b/tests/batch/test_indexer_delivery.py new file mode 100644 index 00000000..06073834 --- /dev/null +++ b/tests/batch/test_indexer_delivery.py @@ -0,0 +1,1809 @@ +""" +Copyright BOOSTRY Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import logging +from datetime import UTC, datetime +from unittest import mock +from unittest.mock import patch + +import pytest +from eth_keyfile import decode_keyfile_json +from sqlalchemy import select +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import Session + +from app.exceptions import ServiceUnavailableError +from app.model.blockchain import IbetShareContract, IbetStraightBondContract +from app.model.blockchain.tx_params.ibet_share import ( + UpdateParams as IbetShareUpdateParams, +) +from app.model.blockchain.tx_params.ibet_straight_bond import ( + UpdateParams as IbetStraightBondUpdateParams, +) +from app.model.db import ( + Account, + DeliveryStatus, + IDXDelivery, + IDXDeliveryBlockNumber, + Notification, + NotificationType, + Token, + TokenType, + TokenVersion, +) +from app.utils.contract_utils import ContractUtils +from app.utils.e2ee_utils import E2EEUtils +from app.utils.web3_utils import AsyncWeb3Wrapper, Web3Wrapper +from batch.indexer_delivery import LOG, Processor, main +from config import CHAIN_ID, TX_GAS_LIMIT +from tests.account_config import config_eth_account +from tests.contract_utils import ( + IbetSecurityTokenContractTestUtils as STContractUtils, + IbetSecurityTokenEscrowContractTestUtils as STEscrowContractUtils, + PersonalInfoContractTestUtils, +) + +web3 = AsyncWeb3Wrapper() + + +@pytest.fixture(scope="function") +def main_func(): + LOG = logging.getLogger("background") + default_log_level = LOG.level + LOG.setLevel(logging.DEBUG) + LOG.propagate = True + yield main + LOG.propagate = False + LOG.setLevel(default_log_level) + + +@pytest.fixture(scope="function") +def processor(db, caplog: pytest.LogCaptureFixture): + LOG = logging.getLogger("background") + default_log_level = LOG.level + LOG.setLevel(logging.DEBUG) + LOG.propagate = True + yield Processor() + LOG.propagate = False + LOG.setLevel(default_log_level) + + +async def deploy_bond_token_contract( + address, + private_key, + personal_info_contract_address, + tradable_exchange_contract_address=None, + transfer_approval_required=None, +): + arguments = [ + "token.name", + "token.symbol", + 100, + 20, + "JPY", + "token.redemption_date", + 30, + "JPY", + "token.return_date", + "token.return_amount", + "token.purpose", + ] + bond_contrat = IbetStraightBondContract() + token_address, _, _ = await bond_contrat.create(arguments, address, private_key) + await bond_contrat.update( + data=IbetStraightBondUpdateParams( + transferable=True, + personal_info_contract_address=personal_info_contract_address, + tradable_exchange_contract_address=tradable_exchange_contract_address, + transfer_approval_required=transfer_approval_required, + ), + tx_from=address, + private_key=private_key, + ) + + return ContractUtils.get_contract("IbetStraightBond", token_address) + + +async def deploy_share_token_contract( + address, + private_key, + personal_info_contract_address, + tradable_exchange_contract_address=None, + transfer_approval_required=None, +): + arguments = [ + "token.name", + "token.symbol", + 20, + 100, + 3, + "token.dividend_record_date", + "token.dividend_payment_date", + "token.cancellation_date", + 30, + ] + share_contract = IbetShareContract() + token_address, _, _ = await share_contract.create(arguments, address, private_key) + await share_contract.update( + data=IbetShareUpdateParams( + transferable=True, + personal_info_contract_address=personal_info_contract_address, + tradable_exchange_contract_address=tradable_exchange_contract_address, + transfer_approval_required=transfer_approval_required, + ), + tx_from=address, + private_key=private_key, + ) + + return ContractUtils.get_contract("IbetShare", token_address) + + +class TestProcessor: + ########################################################################### + # Normal Case + ########################################################################### + + # + # No event log + # - Token not yet issued + @pytest.mark.asyncio + async def test_normal_1_1( + self, + processor, + db, + personal_info_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.rsa_private_key = user_1["rsa_private_key"] + account.rsa_public_key = user_1["rsa_public_key"] + account.rsa_passphrase = E2EEUtils.encrypt("password") + account.rsa_status = 3 + db.add(account) + + # Prepare data : Token(processing token) + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = "test1" + token_1.issuer_address = issuer_address + token_1.abi = "abi" + token_1.tx_hash = "tx_hash" + token_1.token_status = 0 + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + db.commit() + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 0 + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert _idx_delivery_block_number is None + + # + # No event log + # - Issued tokens but no exchange address is set. + @pytest.mark.asyncio + async def test_normal_1_2( + self, processor, db, personal_info_contract, caplog: pytest.LogCaptureFixture + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.rsa_private_key = user_1["rsa_private_key"] + account.rsa_public_key = user_1["rsa_public_key"] + account.rsa_passphrase = E2EEUtils.encrypt("password") + account.rsa_status = 3 + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=None, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + db.commit() + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 0 + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert _idx_delivery_block_number is None + + # + # No event log + # - Issued tokens but the exchange contract other than DVP contract is set. + @pytest.mark.asyncio + async def test_normal_1_3( + self, + processor, + db, + personal_info_contract, + ibet_security_token_escrow_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.rsa_private_key = user_1["rsa_private_key"] + account.rsa_public_key = user_1["rsa_public_key"] + account.rsa_passphrase = E2EEUtils.encrypt("password") + account.rsa_status = 3 + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_escrow_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + db.commit() + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 0 + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert _idx_delivery_block_number.latest_block_number == block_number + assert ( + _idx_delivery_block_number.exchange_address + == ibet_security_token_escrow_contract.address + ) + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_escrow_contract.address}", + ) + ) + == 1 + ) + + # + # Event log + # - Exchange: CreateDelivery (from issuer) + @pytest.mark.asyncio + async def test_normal_2_1( + self, + processor, + db, + personal_info_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : BlockNumber + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = 0 + _idx_delivery_block_number.exchange_address = ( + ibet_security_token_dvp_contract.address + ) + db.add(_idx_delivery_block_number) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_address_1, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_1, tx_receipt_1 = ContractUtils.send_transaction(tx, issuer_private_key) + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 1 + _delivery = _delivery_list[0] + assert _delivery.id == 1 + assert _delivery.exchange_address == ibet_security_token_dvp_contract.address + assert _delivery.token_address == token_address_1 + assert _delivery.buyer_address == user_address_1 + assert _delivery.seller_address == issuer_address + assert _delivery.amount == 30 + assert _delivery.agent_address == agent_address + assert _delivery.data == "." * 1000 + block = await web3.eth.get_block(tx_receipt_1["blockNumber"]) + assert _delivery.create_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.create_transaction_hash == tx_hash_1 + assert _delivery.cancel_blocktimestamp is None + assert _delivery.cancel_transaction_hash is None + assert _delivery.confirm_blocktimestamp is None + assert _delivery.confirm_transaction_hash is None + assert _delivery.finish_blocktimestamp is None + assert _delivery.finish_transaction_hash is None + assert _delivery.abort_blocktimestamp is None + assert _delivery.abort_transaction_hash is None + assert _delivery.confirmed is False + assert _delivery.valid is True + assert _delivery.status == DeliveryStatus.DELIVERY_CREATED + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert ( + _idx_delivery_block_number.exchange_address + == ibet_security_token_dvp_contract.address + ) + assert _idx_delivery_block_number.latest_block_number == block_number + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_dvp_contract.address}", + ) + ) + == 1 + ) + + # + # Event log + # - Exchange: CancelDelivery (from issuer) + @pytest.mark.asyncio + async def test_normal_2_2_1( + self, + processor, + db, + personal_info_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : BlockNumber + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = 0 + _idx_delivery_block_number.exchange_address = ( + ibet_security_token_dvp_contract.address + ) + db.add(_idx_delivery_block_number) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_address_1, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_1, tx_receipt_1 = ContractUtils.send_transaction(tx, issuer_private_key) + + # CancelDelivery + tx = ibet_security_token_dvp_contract.functions.cancelDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_2, tx_receipt_2 = ContractUtils.send_transaction(tx, issuer_private_key) + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 1 + _delivery = _delivery_list[0] + assert _delivery.id == 1 + assert _delivery.exchange_address == ibet_security_token_dvp_contract.address + assert _delivery.token_address == token_address_1 + assert _delivery.buyer_address == user_address_1 + assert _delivery.seller_address == issuer_address + assert _delivery.amount == 30 + assert _delivery.agent_address == agent_address + assert _delivery.data == "." * 1000 + block = await web3.eth.get_block(tx_receipt_1["blockNumber"]) + assert _delivery.create_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.create_transaction_hash == tx_hash_1 + block = await web3.eth.get_block(tx_receipt_2["blockNumber"]) + assert _delivery.cancel_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.cancel_transaction_hash == tx_hash_2 + assert _delivery.confirm_blocktimestamp is None + assert _delivery.confirm_transaction_hash is None + assert _delivery.finish_blocktimestamp is None + assert _delivery.finish_transaction_hash is None + assert _delivery.abort_blocktimestamp is None + assert _delivery.abort_transaction_hash is None + assert _delivery.confirmed is False + assert _delivery.valid is False + assert _delivery.status == DeliveryStatus.DELIVERY_CANCELED + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert ( + _idx_delivery_block_number.exchange_address + == ibet_security_token_dvp_contract.address + ) + assert _idx_delivery_block_number.latest_block_number == block_number + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_dvp_contract.address}", + ) + ) + == 1 + ) + + # + # Event log + # - Exchange: CancelDelivery (from buyer) + @pytest.mark.asyncio + async def test_normal_2_2_2( + self, + processor, + db, + personal_info_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : BlockNumber + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = 0 + _idx_delivery_block_number.exchange_address = ( + ibet_security_token_dvp_contract.address + ) + db.add(_idx_delivery_block_number) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_address_1, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_1, tx_receipt_1 = ContractUtils.send_transaction(tx, issuer_private_key) + + # CancelDelivery + tx = ibet_security_token_dvp_contract.functions.cancelDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": user_address_1, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_2, tx_receipt_2 = ContractUtils.send_transaction(tx, user_private_key_1) + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 1 + _delivery = _delivery_list[0] + assert _delivery.id == 1 + assert _delivery.exchange_address == ibet_security_token_dvp_contract.address + assert _delivery.token_address == token_address_1 + assert _delivery.buyer_address == user_address_1 + assert _delivery.seller_address == issuer_address + assert _delivery.amount == 30 + assert _delivery.agent_address == agent_address + assert _delivery.data == "." * 1000 + block = await web3.eth.get_block(tx_receipt_1["blockNumber"]) + assert _delivery.create_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.create_transaction_hash == tx_hash_1 + block = await web3.eth.get_block(tx_receipt_2["blockNumber"]) + assert _delivery.cancel_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.cancel_transaction_hash == tx_hash_2 + assert _delivery.confirm_blocktimestamp is None + assert _delivery.confirm_transaction_hash is None + assert _delivery.finish_blocktimestamp is None + assert _delivery.finish_transaction_hash is None + assert _delivery.abort_blocktimestamp is None + assert _delivery.abort_transaction_hash is None + assert _delivery.confirmed is False + assert _delivery.valid is False + assert _delivery.status == DeliveryStatus.DELIVERY_CANCELED + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert ( + _idx_delivery_block_number.exchange_address + == ibet_security_token_dvp_contract.address + ) + assert _idx_delivery_block_number.latest_block_number == block_number + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_dvp_contract.address}", + ) + ) + == 1 + ) + + # + # Event log + # - Exchange: ConfirmDelivery (from buyer) + @pytest.mark.freeze_time("2021-04-27 12:34:56") + @pytest.mark.asyncio + async def test_normal_2_3( + self, + processor, + db, + personal_info_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : BlockNumber + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = 0 + _idx_delivery_block_number.exchange_address = ( + ibet_security_token_dvp_contract.address + ) + db.add(_idx_delivery_block_number) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_address_1, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_1, tx_receipt_1 = ContractUtils.send_transaction(tx, issuer_private_key) + + # ConfirmDelivery + tx = ibet_security_token_dvp_contract.functions.confirmDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": user_address_1, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_2, tx_receipt_2 = ContractUtils.send_transaction(tx, user_private_key_1) + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 1 + _delivery = _delivery_list[0] + assert _delivery.id == 1 + assert _delivery.exchange_address == ibet_security_token_dvp_contract.address + assert _delivery.token_address == token_address_1 + assert _delivery.buyer_address == user_address_1 + assert _delivery.seller_address == issuer_address + assert _delivery.amount == 30 + assert _delivery.agent_address == agent_address + assert _delivery.data == "." * 1000 + block = await web3.eth.get_block(tx_receipt_1["blockNumber"]) + assert _delivery.create_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.create_transaction_hash == tx_hash_1 + assert _delivery.cancel_blocktimestamp is None + assert _delivery.cancel_transaction_hash is None + block = await web3.eth.get_block(tx_receipt_2["blockNumber"]) + assert _delivery.confirm_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.confirm_transaction_hash == tx_hash_2 + assert _delivery.finish_blocktimestamp is None + assert _delivery.finish_transaction_hash is None + assert _delivery.abort_blocktimestamp is None + assert _delivery.abort_transaction_hash is None + assert _delivery.confirmed is True + assert _delivery.valid is True + assert _delivery.status == DeliveryStatus.DELIVERY_CONFIRMED + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert ( + _idx_delivery_block_number.exchange_address + == ibet_security_token_dvp_contract.address + ) + assert _idx_delivery_block_number.latest_block_number == block_number + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_dvp_contract.address}", + ) + ) + == 1 + ) + + # + # Event log + # - Exchange: FinishDelivery (from agent) + @pytest.mark.freeze_time("2021-04-27 12:34:56") + @pytest.mark.asyncio + async def test_normal_2_4( + self, + processor, + db, + personal_info_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : BlockNumber + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = 0 + _idx_delivery_block_number.exchange_address = ( + ibet_security_token_dvp_contract.address + ) + db.add(_idx_delivery_block_number) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_address_1, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_1, tx_receipt_1 = ContractUtils.send_transaction(tx, issuer_private_key) + + # ConfirmDelivery + tx = ibet_security_token_dvp_contract.functions.confirmDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": user_address_1, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_2, tx_receipt_2 = ContractUtils.send_transaction(tx, user_private_key_1) + + # FinishDelivery + tx = ibet_security_token_dvp_contract.functions.finishDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": agent_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_3, tx_receipt_3 = ContractUtils.send_transaction(tx, agent_private_key) + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 1 + _delivery = _delivery_list[0] + assert _delivery.id == 1 + assert _delivery.exchange_address == ibet_security_token_dvp_contract.address + assert _delivery.token_address == token_address_1 + assert _delivery.buyer_address == user_address_1 + assert _delivery.seller_address == issuer_address + assert _delivery.amount == 30 + assert _delivery.agent_address == agent_address + assert _delivery.data == "." * 1000 + block = await web3.eth.get_block(tx_receipt_1["blockNumber"]) + assert _delivery.create_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.create_transaction_hash == tx_hash_1 + assert _delivery.cancel_blocktimestamp is None + assert _delivery.cancel_transaction_hash is None + block = await web3.eth.get_block(tx_receipt_2["blockNumber"]) + assert _delivery.confirm_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.confirm_transaction_hash == tx_hash_2 + block = await web3.eth.get_block(tx_receipt_3["blockNumber"]) + assert _delivery.finish_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.finish_transaction_hash == tx_hash_3 + assert _delivery.abort_blocktimestamp is None + assert _delivery.abort_transaction_hash is None + assert _delivery.confirmed is True + assert _delivery.valid is False + assert _delivery.status == DeliveryStatus.DELIVERY_FINISHED + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert ( + _idx_delivery_block_number.exchange_address + == ibet_security_token_dvp_contract.address + ) + assert _idx_delivery_block_number.latest_block_number == block_number + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_dvp_contract.address}", + ) + ) + == 1 + ) + + # + # Event log + # - Exchange: AbortDelivery (from agent) + @pytest.mark.asyncio + async def test_normal_2_5( + self, + processor, + db, + personal_info_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : BlockNumber + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = 0 + _idx_delivery_block_number.exchange_address = ( + ibet_security_token_dvp_contract.address + ) + db.add(_idx_delivery_block_number) + + db.commit() + + # Transfer + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_address_1, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_1, tx_receipt_1 = ContractUtils.send_transaction(tx, issuer_private_key) + + # ConfirmDelivery + tx = ibet_security_token_dvp_contract.functions.confirmDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": user_address_1, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_2, tx_receipt_2 = ContractUtils.send_transaction(tx, user_private_key_1) + + # FinishDelivery + tx = ibet_security_token_dvp_contract.functions.abortDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": agent_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_3, tx_receipt_3 = ContractUtils.send_transaction(tx, agent_private_key) + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 1 + _delivery = _delivery_list[0] + assert _delivery.id == 1 + assert _delivery.exchange_address == ibet_security_token_dvp_contract.address + assert _delivery.token_address == token_address_1 + assert _delivery.buyer_address == user_address_1 + assert _delivery.seller_address == issuer_address + assert _delivery.amount == 30 + assert _delivery.agent_address == agent_address + assert _delivery.data == "." * 1000 + block = await web3.eth.get_block(tx_receipt_1["blockNumber"]) + assert _delivery.create_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.create_transaction_hash == tx_hash_1 + assert _delivery.cancel_blocktimestamp is None + assert _delivery.cancel_transaction_hash is None + block = await web3.eth.get_block(tx_receipt_2["blockNumber"]) + assert _delivery.confirm_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.confirm_transaction_hash == tx_hash_2 + assert _delivery.finish_blocktimestamp is None + assert _delivery.finish_transaction_hash is None + block = await web3.eth.get_block(tx_receipt_3["blockNumber"]) + assert _delivery.abort_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.abort_transaction_hash == tx_hash_3 + assert _delivery.confirmed is True + assert _delivery.valid is False + assert _delivery.status == DeliveryStatus.DELIVERY_ABORTED + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).limit(1) + ).first() + assert ( + _idx_delivery_block_number.exchange_address + == ibet_security_token_dvp_contract.address + ) + assert _idx_delivery_block_number.latest_block_number == block_number + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_dvp_contract.address}", + ) + ) + == 1 + ) + + # + # Multi Exchange + @pytest.mark.asyncio + async def test_normal_3( + self, + processor, + db, + personal_info_contract, + ibet_security_token_escrow_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + agent_address = user_3["address"] + agent_private_key = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_escrow_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token + token_contract_2 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + token_address_2 = token_contract_2.address + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = token_address_2 + token_2.issuer_address = issuer_address + token_2.abi = token_contract_2.abi + token_2.tx_hash = "tx_hash" + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : BlockNumber + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = 0 + _idx_delivery_block_number.exchange_address = ( + ibet_security_token_dvp_contract.address + ) + db.add(_idx_delivery_block_number) + + db.commit() + + # Transfer + tx = token_contract_2.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # CreateDelivery + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_address_2, user_address_1, 30, agent_address, "." * 1000 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_1, tx_receipt_1 = ContractUtils.send_transaction(tx, issuer_private_key) + + # ConfirmDelivery + tx = ibet_security_token_dvp_contract.functions.confirmDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": user_address_1, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_2, tx_receipt_2 = ContractUtils.send_transaction(tx, user_private_key_1) + + # FinishDelivery + tx = ibet_security_token_dvp_contract.functions.abortDelivery( + 1 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": agent_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + tx_hash_3, tx_receipt_3 = ContractUtils.send_transaction(tx, agent_private_key) + + # Run target process + block_number = await web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _delivery_list = db.scalars(select(IDXDelivery)).all() + assert len(_delivery_list) == 1 + _delivery = _delivery_list[0] + assert _delivery.id == 1 + assert _delivery.exchange_address == ibet_security_token_dvp_contract.address + assert _delivery.token_address == token_address_2 + assert _delivery.buyer_address == user_address_1 + assert _delivery.seller_address == issuer_address + assert _delivery.amount == 30 + assert _delivery.agent_address == agent_address + assert _delivery.data == "." * 1000 + block = await web3.eth.get_block(tx_receipt_1["blockNumber"]) + assert _delivery.create_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.create_transaction_hash == tx_hash_1 + assert _delivery.cancel_blocktimestamp is None + assert _delivery.cancel_transaction_hash is None + block = await web3.eth.get_block(tx_receipt_2["blockNumber"]) + assert _delivery.confirm_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.confirm_transaction_hash == tx_hash_2 + assert _delivery.finish_blocktimestamp is None + assert _delivery.finish_transaction_hash is None + block = await web3.eth.get_block(tx_receipt_3["blockNumber"]) + assert _delivery.abort_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) + assert _delivery.abort_transaction_hash == tx_hash_3 + assert _delivery.confirmed is True + assert _delivery.valid is False + assert _delivery.status == DeliveryStatus.DELIVERY_ABORTED + + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).where( + IDXDeliveryBlockNumber.exchange_address + == ibet_security_token_escrow_contract.address + ) + ).first() + assert _idx_delivery_block_number.latest_block_number == block_number + _idx_delivery_block_number = db.scalars( + select(IDXDeliveryBlockNumber).where( + IDXDeliveryBlockNumber.exchange_address + == ibet_security_token_dvp_contract.address + ) + ).first() + assert _idx_delivery_block_number.latest_block_number == block_number + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_escrow_contract.address}", + ) + ) + == 1 + ) + + assert ( + caplog.record_tuples.count( + ( + LOG.name, + logging.INFO, + f"Syncing from=1, to={block_number}, exchange={ibet_security_token_dvp_contract.address}", + ) + ) + == 1 + ) + + # + # If block number processed in batch is equal or greater than current block number, + # batch will output a log "skip process". + @mock.patch("web3.eth.Eth.block_number", 100) + @pytest.mark.asyncio + async def test_normal_4( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_private_key_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + user_address_2 = user_3["address"] + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(processing token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.token_status = 0 + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : BlockNumber + _idx_delivery_block_number = IDXDeliveryBlockNumber() + _idx_delivery_block_number.latest_block_number = 100 + _idx_delivery_block_number.exchange_address = ( + ibet_security_token_dvp_contract.address + ) + db.add(_idx_delivery_block_number) + + db.commit() + + await processor.sync_new_logs() + assert ( + caplog.record_tuples.count((LOG.name, logging.DEBUG, "skip process")) == 1 + ) + + # + # Newly tokens added + @pytest.mark.asyncio + async def test_normal_6( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ibet_security_token_escrow_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Issuer issues bond token. + token_contract1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_escrow_contract.address, + ) + token_address_1 = token_contract1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + db.commit() + + # Run target process + await processor.sync_new_logs() + + # Assertion + assert len(processor.token_list) == 1 + assert len(processor.exchange_list) == 1 + + # Prepare additional token + token_contract2 = await deploy_share_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + transfer_approval_required=False, + ) + token_address_2 = token_contract2.address + token_2 = Token() + token_2.type = TokenType.IBET_SHARE.value + token_2.token_address = token_address_2 + token_2.issuer_address = issuer_address + token_2.abi = token_contract2.abi + token_2.tx_hash = "tx_hash" + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + db.commit() + + # Run target process + await processor.sync_new_logs() + + # Assertion + # newly issued token is loaded properly + assert len(processor.token_list) == 2 + assert len(processor.exchange_list) == 2 + + ########################################################################### + # Error Case + ########################################################################### + + # + # If each error occurs, batch will output logs and continue next sync. + @pytest.mark.asyncio + async def test_error_1( + self, + main_func, + db: Session, + personal_info_contract, + caplog: pytest.LogCaptureFixture, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract = await deploy_bond_token_contract( + issuer_address, issuer_private_key, personal_info_contract.address + ) + token_address = token_contract.address + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.token_address = token_address + token.issuer_address = issuer_address + token.abi = token_contract.abi + token.tx_hash = "tx_hash" + token.version = TokenVersion.V_24_06 + db.add(token) + + db.commit() + + # Run mainloop once and fail with web3 utils error + with patch("batch.indexer_delivery.INDEXER_SYNC_INTERVAL", None), patch.object( + AsyncWeb3Wrapper().eth, "contract", side_effect=ServiceUnavailableError() + ), pytest.raises(TypeError): + await main_func() + assert 1 == caplog.record_tuples.count( + (LOG.name, logging.WARNING, "An external service was unavailable") + ) + caplog.clear() + + # Run mainloop once and fail with sqlalchemy Error + with patch("batch.indexer_delivery.INDEXER_SYNC_INTERVAL", None), patch.object( + AsyncSession, "commit", side_effect=SQLAlchemyError(code="dbapi") + ), pytest.raises(TypeError): + await main_func() + assert "A database error has occurred: code=dbapi" in caplog.text + caplog.clear() diff --git a/tests/test_batch_indexer_e2e_messaging.py b/tests/batch/test_indexer_e2e_messaging.py similarity index 87% rename from tests/test_batch_indexer_e2e_messaging.py rename to tests/batch/test_indexer_e2e_messaging.py index 129e2a34..eef12eb7 100644 --- a/tests/test_batch_indexer_e2e_messaging.py +++ b/tests/batch/test_indexer_e2e_messaging.py @@ -20,8 +20,7 @@ import base64 import json import os -import time -from datetime import datetime +from datetime import UTC, datetime import pytest from Crypto import Random @@ -175,18 +174,6 @@ async def test_normal_2_1(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message _type = "test_type" message = { @@ -194,6 +181,7 @@ async def test_normal_2_1(self, processor, db, e2e_messaging_contract): "address": "東京都1", } message_message_str = json.dumps(message) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( e2e_messaging_contract.address ).send_message_external( @@ -205,7 +193,22 @@ async def test_normal_2_1(self, processor, db, e2e_messaging_contract): user_private_key_2, ) sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) - sending_block_timestamp = datetime.utcfromtimestamp(sending_block["timestamp"]) + sending_block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"], UTC + ).replace(tzinfo=None) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -250,15 +253,39 @@ async def test_normal_2_2(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) + # Send Message + _type = "test_type" + message = { + "name": "テスト太郎1", + "address": "東京都1", + } + message_message_str = json.dumps(message) + + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message_external( + user_address_1, + _type, + message_message_str, + self.rsa_public_key, + user_address_2, + user_private_key_2, + ) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + sending_block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"], UTC + ).replace(tzinfo=None) + # Prepare data : E2EMessagingAccountRsaKey _e2e_account_rsa_key = E2EMessagingAccountRsaKey() _e2e_account_rsa_key.account_address = user_address_1 _e2e_account_rsa_key.rsa_private_key = "test1" _e2e_account_rsa_key.rsa_public_key = "test1" _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 3, UTC + ).replace(tzinfo=None) db.add(_e2e_account_rsa_key) - time.sleep(1) # Prepare data : E2EMessagingAccountRsaKey _e2e_account_rsa_key = E2EMessagingAccountRsaKey() @@ -266,9 +293,10 @@ async def test_normal_2_2(self, processor, db, e2e_messaging_contract): _e2e_account_rsa_key.rsa_private_key = "test2" _e2e_account_rsa_key.rsa_public_key = "test2" _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 2, UTC + ).replace(tzinfo=None) db.add(_e2e_account_rsa_key) - time.sleep(1) # Prepare data : E2EMessagingAccountRsaKey _e2e_account_rsa_key = E2EMessagingAccountRsaKey() @@ -276,29 +304,10 @@ async def test_normal_2_2(self, processor, db, e2e_messaging_contract): _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) db.add(_e2e_account_rsa_key) - time.sleep(1) - - # Send Message - _type = "test_type" - message = { - "name": "テスト太郎1", - "address": "東京都1", - } - message_message_str = json.dumps(message) - sending_tx_hash, sending_tx_receipt = await E2EMessaging( - e2e_messaging_contract.address - ).send_message_external( - user_address_1, - _type, - message_message_str, - self.rsa_public_key, - user_address_2, - user_private_key_2, - ) - sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) - sending_block_timestamp = datetime.utcfromtimestamp(sending_block["timestamp"]) # Prepare data : E2EMessagingAccountRsaKey _e2e_account_rsa_key = E2EMessagingAccountRsaKey() @@ -306,9 +315,10 @@ async def test_normal_2_2(self, processor, db, e2e_messaging_contract): _e2e_account_rsa_key.rsa_private_key = "test3" _e2e_account_rsa_key.rsa_public_key = "test3" _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] + 1, UTC + ).replace(tzinfo=None) db.add(_e2e_account_rsa_key) - time.sleep(1) # Prepare data : E2EMessagingAccountRsaKey _e2e_account_rsa_key = E2EMessagingAccountRsaKey() @@ -316,10 +326,10 @@ async def test_normal_2_2(self, processor, db, e2e_messaging_contract): _e2e_account_rsa_key.rsa_private_key = "test4" _e2e_account_rsa_key.rsa_public_key = "test4" _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] + 2, UTC + ).replace(tzinfo=None) db.add(_e2e_account_rsa_key) - time.sleep(1) - db.commit() # Run target process @@ -366,34 +376,12 @@ async def test_normal_3(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - # Prepare data : E2EMessagingAccount _e2e_account = E2EMessagingAccount() _e2e_account.account_address = user_address_2 _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_2 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message(user3 -> user1) _type_1 = "test_type1" message = { @@ -401,6 +389,7 @@ async def test_normal_3(self, processor, db, e2e_messaging_contract): "address": "東京都1", } message_message_str_1 = json.dumps(message) + sending_tx_hash_1, sending_tx_receipt = await E2EMessaging( e2e_messaging_contract.address ).send_message_external( @@ -411,15 +400,16 @@ async def test_normal_3(self, processor, db, e2e_messaging_contract): user_address_3, user_private_key_3, ) - sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) - sending_block_timestamp_1 = datetime.utcfromtimestamp( - sending_block["timestamp"] - ) + sending_block_1 = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + sending_block_timestamp_1 = datetime.fromtimestamp( + sending_block_1["timestamp"], UTC + ).replace(tzinfo=None) # Send Message(user3 -> user2) _type_2 = "test_type2" message = ["テスト太郎2", "東京都2"] message_message_str_2 = json.dumps(message) + sending_tx_hash_2, sending_tx_receipt = await E2EMessaging( e2e_messaging_contract.address ).send_message_external( @@ -430,14 +420,15 @@ async def test_normal_3(self, processor, db, e2e_messaging_contract): user_address_3, user_private_key_3, ) - sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) - sending_block_timestamp_2 = datetime.utcfromtimestamp( - sending_block["timestamp"] - ) + sending_block_2 = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + sending_block_timestamp_2 = datetime.fromtimestamp( + sending_block_2["timestamp"], UTC + ).replace(tzinfo=None) # Send Message(user3 -> user1) _type_3 = "test_type3" message_message_str_3 = "テスト太郎1,東京都1" + sending_tx_hash_3, sending_tx_receipt = await E2EMessaging( e2e_messaging_contract.address ).send_message_external( @@ -448,14 +439,15 @@ async def test_normal_3(self, processor, db, e2e_messaging_contract): user_address_3, user_private_key_3, ) - sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) - sending_block_timestamp_3 = datetime.utcfromtimestamp( - sending_block["timestamp"] - ) + sending_block_3 = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + sending_block_timestamp_3 = datetime.fromtimestamp( + sending_block_3["timestamp"], UTC + ).replace(tzinfo=None) # Send Message(user3 -> user2) _type_4 = "a" * 50 message_message_str_4 = "a" * 5000 + sending_tx_hash_4, sending_tx_receipt = await E2EMessaging( e2e_messaging_contract.address ).send_message_external( @@ -466,10 +458,34 @@ async def test_normal_3(self, processor, db, e2e_messaging_contract): user_address_3, user_private_key_3, ) - sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) - sending_block_timestamp_4 = datetime.utcfromtimestamp( - sending_block["timestamp"] - ) + sending_block_4 = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + sending_block_timestamp_4 = datetime.fromtimestamp( + sending_block_4["timestamp"], UTC + ).replace(tzinfo=None) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block_1["timestamp"] - 2, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_2 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block_1["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -542,18 +558,6 @@ async def test_normal_4(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message _type = "test_type" message = { @@ -561,7 +565,10 @@ async def test_normal_4(self, processor, db, e2e_messaging_contract): "address": "東京都1", } message_message_str = json.dumps(message) - await E2EMessaging(e2e_messaging_contract.address).send_message_external( + + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message_external( user_address_3, # not target _type, message_message_str, @@ -569,6 +576,20 @@ async def test_normal_4(self, processor, db, e2e_messaging_contract): user_address_2, user_private_key_2, ) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -608,24 +629,26 @@ async def test_error_1_1(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) + # Send Message + message = "test" + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + # Prepare data : E2EMessagingAccountRsaKey _e2e_account_rsa_key = E2EMessagingAccountRsaKey() _e2e_account_rsa_key.account_address = user_address_1 _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) db.add(_e2e_account_rsa_key) - time.sleep(1) db.commit() - # Send Message - message = "test" - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) - # Run target process block_number = web3.eth.block_number await processor.process() @@ -660,18 +683,6 @@ async def test_error_1_2(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message aes_key = os.urandom(32) aes_iv = os.urandom(16) @@ -691,9 +702,23 @@ async def test_error_1_2(self, processor, db, e2e_messaging_contract): } } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -729,28 +754,30 @@ async def test_error_1_3(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) + # Send Message + message = json.dumps( + { + "type": "test_type", + } + ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + # Prepare data : E2EMessagingAccountRsaKey _e2e_account_rsa_key = E2EMessagingAccountRsaKey() _e2e_account_rsa_key.account_address = user_address_1 _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) db.add(_e2e_account_rsa_key) - time.sleep(1) db.commit() - # Send Message - message = json.dumps( - { - "type": "test_type", - } - ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) - # Run target process block_number = web3.eth.block_number await processor.process() @@ -785,18 +812,6 @@ async def test_error_1_4(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message aes_key = os.urandom(32) aes_iv = os.urandom(16) @@ -814,9 +829,23 @@ async def test_error_1_4(self, processor, db, e2e_messaging_contract): "text": {"cipher_key": cipher_key, "message": encrypted_message}, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -852,18 +881,6 @@ async def test_error_1_5(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message aes_key = os.urandom(32) aes_iv = os.urandom(16) @@ -880,9 +897,23 @@ async def test_error_1_5(self, processor, db, e2e_messaging_contract): }, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -918,16 +949,6 @@ async def test_error_1_6(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - db.commit() # Send Message @@ -943,9 +964,21 @@ async def test_error_1_6(self, processor, db, e2e_messaging_contract): }, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) # Run target process block_number = web3.eth.block_number @@ -981,18 +1014,6 @@ async def test_error_1_7(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message aes_key = os.urandom(32) aes_iv = os.urandom(16) @@ -1010,9 +1031,23 @@ async def test_error_1_7(self, processor, db, e2e_messaging_contract): "text": {"cipher_key": cipher_key, "message": encrypted_message}, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -1054,7 +1089,10 @@ async def test_error_2(self, processor, db, e2e_messaging_contract): "address": "東京都1", } message_message_str = json.dumps(message) - await E2EMessaging(e2e_messaging_contract.address).send_message_external( + + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message_external( user_address_1, _type, message_message_str, @@ -1062,7 +1100,7 @@ async def test_error_2(self, processor, db, e2e_messaging_contract): user_address_2, user_private_key_2, ) - time.sleep(1) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) # Prepare data : E2EMessagingAccountRsaKey _e2e_account_rsa_key = E2EMessagingAccountRsaKey() @@ -1070,8 +1108,10 @@ async def test_error_2(self, processor, db, e2e_messaging_contract): _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = ( - datetime.utcnow() + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] + 1, UTC + ).replace( + tzinfo=None ) # Registry after send message db.add(_e2e_account_rsa_key) @@ -1111,18 +1151,6 @@ async def test_error_3_1(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message aes_key = os.urandom(32) aes_iv = os.urandom(16) @@ -1137,9 +1165,23 @@ async def test_error_3_1(self, processor, db, e2e_messaging_contract): "text": {"cipher_key": "cipher_key", "message": encrypted_message}, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -1175,18 +1217,6 @@ async def test_error_3_2(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message aes_key = os.urandom(32) aes_iv = os.urandom(16) @@ -1202,9 +1232,23 @@ async def test_error_3_2(self, processor, db, e2e_messaging_contract): "text": {"cipher_key": cipher_key, "message": encrypted_message}, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -1240,18 +1284,6 @@ async def test_error_3_3(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message random_func = Random.new().read rsa = RSA.generate(4096, random_func) @@ -1272,9 +1304,23 @@ async def test_error_3_3(self, processor, db, e2e_messaging_contract): "text": {"cipher_key": cipher_key, "message": encrypted_message}, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -1310,18 +1356,6 @@ async def test_error_3_4(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message aes_key = os.urandom(32) rsa_key = RSA.import_key(self.rsa_public_key) @@ -1333,9 +1367,23 @@ async def test_error_3_4(self, processor, db, e2e_messaging_contract): "text": {"cipher_key": cipher_key, "message": "test_message"}, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number @@ -1371,18 +1419,6 @@ async def test_error_3_5(self, processor, db, e2e_messaging_contract): _e2e_account.is_deleted = False db.add(_e2e_account) - # Prepare data : E2EMessagingAccountRsaKey - _e2e_account_rsa_key = E2EMessagingAccountRsaKey() - _e2e_account_rsa_key.account_address = user_address_1 - _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key - _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key - _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) - _e2e_account_rsa_key.block_timestamp = datetime.utcnow() - db.add(_e2e_account_rsa_key) - time.sleep(1) - - db.commit() - # Send Message aes_key = os.urandom(32) aes_iv = os.urandom(16) @@ -1401,9 +1437,23 @@ async def test_error_3_5(self, processor, db, e2e_messaging_contract): "text": {"cipher_key": cipher_key, "message": encrypted_message}, } ) - await E2EMessaging(e2e_messaging_contract.address).send_message( - user_address_1, message, user_address_2, user_private_key_2 - ) + sending_tx_hash, sending_tx_receipt = await E2EMessaging( + e2e_messaging_contract.address + ).send_message(user_address_1, message, user_address_2, user_private_key_2) + sending_block = web3.eth.get_block(sending_tx_receipt["blockNumber"]) + + # Prepare data : E2EMessagingAccountRsaKey + _e2e_account_rsa_key = E2EMessagingAccountRsaKey() + _e2e_account_rsa_key.account_address = user_address_1 + _e2e_account_rsa_key.rsa_private_key = self.rsa_private_key + _e2e_account_rsa_key.rsa_public_key = self.rsa_public_key + _e2e_account_rsa_key.rsa_passphrase = E2EEUtils.encrypt(self.rsa_passphrase) + _e2e_account_rsa_key.block_timestamp = datetime.fromtimestamp( + sending_block["timestamp"] - 1, UTC + ).replace(tzinfo=None) + db.add(_e2e_account_rsa_key) + + db.commit() # Run target process block_number = web3.eth.block_number diff --git a/tests/test_batch_indexer_issue_redeem.py b/tests/batch/test_indexer_issue_redeem.py similarity index 89% rename from tests/test_batch_indexer_issue_redeem.py rename to tests/batch/test_indexer_issue_redeem.py index e575166d..d60f5ec9 100644 --- a/tests/test_batch_indexer_issue_redeem.py +++ b/tests/batch/test_indexer_issue_redeem.py @@ -18,7 +18,7 @@ """ import logging -from datetime import datetime +from datetime import UTC, datetime from unittest import mock from unittest.mock import patch @@ -169,7 +169,7 @@ async def test_normal_1(self, processor, db, personal_info_contract): token_1.abi = "abi" token_1.tx_hash = "tx_hash" token_1.token_status = 0 - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -211,7 +211,7 @@ async def test_normal_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -262,7 +262,7 @@ async def test_normal_3_1(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -296,7 +296,9 @@ async def test_normal_3_1(self, processor, db, personal_info_contract): assert event_0.target_address == issuer_address assert event_0.amount == 40 block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert event_0.block_timestamp == datetime.utcfromtimestamp(block["timestamp"]) + assert event_0.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) idx_block_number = db.scalars( select(IDXIssueRedeemBlockNumber).limit(1) @@ -336,7 +338,7 @@ async def test_normal_3_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -370,7 +372,9 @@ async def test_normal_3_2(self, processor, db, personal_info_contract): assert event_0.target_address == issuer_address assert event_0.amount == 40 block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert event_0.block_timestamp == datetime.utcfromtimestamp(block["timestamp"]) + assert event_0.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) idx_block_number = db.scalars( select(IDXIssueRedeemBlockNumber).limit(1) @@ -410,7 +414,7 @@ async def test_normal_4_1(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -444,7 +448,9 @@ async def test_normal_4_1(self, processor, db, personal_info_contract): assert event_0.target_address == issuer_address assert event_0.amount == 10 block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert event_0.block_timestamp == datetime.utcfromtimestamp(block["timestamp"]) + assert event_0.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) idx_block_number = db.scalars( select(IDXIssueRedeemBlockNumber).limit(1) @@ -484,7 +490,7 @@ async def test_normal_4_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -518,7 +524,9 @@ async def test_normal_4_2(self, processor, db, personal_info_contract): assert event_0.target_address == issuer_address assert event_0.amount == 10 block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert event_0.block_timestamp == datetime.utcfromtimestamp(block["timestamp"]) + assert event_0.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) idx_block_number = db.scalars( select(IDXIssueRedeemBlockNumber).limit(1) @@ -557,7 +565,7 @@ async def test_normal_5(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -604,7 +612,9 @@ async def test_normal_5(self, processor, db, personal_info_contract): assert event_0.target_address == issuer_address assert event_0.amount == 10 block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert event_0.block_timestamp == datetime.utcfromtimestamp(block["timestamp"]) + assert event_0.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) event_1 = event_list[1] assert event_1.id == 2 @@ -615,7 +625,9 @@ async def test_normal_5(self, processor, db, personal_info_contract): assert event_1.target_address == issuer_address assert event_1.amount == 20 block = web3.eth.get_block(tx_receipt_2["blockNumber"]) - assert event_1.block_timestamp == datetime.utcfromtimestamp(block["timestamp"]) + assert event_1.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) idx_block_number = db.scalars( select(IDXIssueRedeemBlockNumber).limit(1) @@ -624,77 +636,11 @@ async def test_normal_5(self, processor, db, personal_info_contract): assert idx_block_number.latest_block_number == block_number # Normal_6 - # If DB session fails in phase sinking each event, batch outputs logs exception occurred. - @pytest.mark.asyncio - async def test_normal_6( - self, - processor: Processor, - db: Session, - personal_info_contract, - caplog: pytest.LogCaptureFixture, - ): - user_1 = config_eth_account("user1") - issuer_address = user_1["address"] - issuer_private_key = decode_keyfile_json( - raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") - ) - - # Prepare data : Account - account = Account() - account.issuer_address = issuer_address - account.keyfile = user_1["keyfile_json"] - account.eoa_password = E2EEUtils.encrypt("password") - db.add(account) - - # Prepare data : Token - token_contract_1 = await deploy_bond_token_contract( - address=issuer_address, - private_key=issuer_private_key, - personal_info_contract_address=personal_info_contract.address, - ) - - token_address_1 = token_contract_1.address - token_1 = Token() - token_1.type = TokenType.IBET_STRAIGHT_BOND.value - token_1.token_address = token_address_1 - token_1.issuer_address = issuer_address - token_1.abi = token_contract_1.abi - token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 - db.add(token_1) - - db.commit() - - # issueFrom - tx = token_contract_1.functions.issueFrom( - issuer_address, config.ZERO_ADDRESS, 10 - ).build_transaction( - { - "chainId": CHAIN_ID, - "from": issuer_address, - "gas": TX_GAS_LIMIT, - "gasPrice": 0, - } - ) - ContractUtils.send_transaction(tx, issuer_private_key) - - with patch.object(Session, "add", side_effect=Exception()): - await processor.sync_new_logs() - - assert 1 == caplog.record_tuples.count( - ( - LOG.name, - logging.ERROR, - "An exception occurred during event synchronization", - ) - ) - - # Normal_7 # If block number processed in batch is equal or greater than current block number, # batch logs "skip process". @pytest.mark.asyncio @mock.patch("web3.eth.Eth.block_number", 100) - async def test_normal_7( + async def test_normal_6( self, processor: Processor, db: Session, caplog: pytest.LogCaptureFixture ): _idx_position_bond_block_number = IDXIssueRedeemBlockNumber() @@ -708,10 +654,10 @@ async def test_normal_7( (LOG.name, logging.DEBUG, "skip process") ) - # Normal_8 + # Normal_7 # Newly tokens added @pytest.mark.asyncio - async def test_normal_8( + async def test_normal_7( self, processor: Processor, db: Session, personal_info_contract ): user_1 = config_eth_account("user1") @@ -741,7 +687,7 @@ async def test_normal_8( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -766,7 +712,7 @@ async def test_normal_8( token_2.issuer_address = issuer_address token_2.abi = token_contract_2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -819,7 +765,7 @@ async def test_error_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() diff --git a/tests/test_batch_indexer_personal_info.py b/tests/batch/test_indexer_personal_info.py similarity index 87% rename from tests/test_batch_indexer_personal_info.py rename to tests/batch/test_indexer_personal_info.py index 81f5ae59..b06a13a0 100644 --- a/tests/test_batch_indexer_personal_info.py +++ b/tests/batch/test_indexer_personal_info.py @@ -41,6 +41,8 @@ Account, IDXPersonalInfo, IDXPersonalInfoBlockNumber, + IDXPersonalInfoHistory, + PersonalInfoEventType, Token, TokenType, TokenVersion, @@ -153,7 +155,7 @@ async def test_normal_1_1(self, processor, db, personal_info_contract): token_1.abi = "abi" token_1.tx_hash = "tx_hash" token_1.token_status = 0 - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -165,12 +167,16 @@ async def test_normal_1_1(self, processor, db, personal_info_contract): # Assertion _personal_info_list = db.scalars(select(IDXPersonalInfo)).all() assert len(_personal_info_list) == 0 + _idx_personal_info_block_number = db.scalars( select(IDXPersonalInfoBlockNumber).limit(1) ).first() assert _idx_personal_info_block_number.id == 1 assert _idx_personal_info_block_number.latest_block_number == block_number + _personal_info_history_list = db.scalars(select(IDXPersonalInfoHistory)).all() + assert len(_personal_info_history_list) == 0 + # # Single Token # No event logs @@ -206,7 +212,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -217,7 +223,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : BlockNumber @@ -234,12 +240,16 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): # Assertion _personal_info_list = db.scalars(select(IDXPersonalInfo)).all() assert len(_personal_info_list) == 0 + _idx_personal_info_block_number = db.scalars( select(IDXPersonalInfoBlockNumber).limit(1) ).first() assert _idx_personal_info_block_number.id == 1 assert _idx_personal_info_block_number.latest_block_number == block_number + _personal_info_history_list = db.scalars(select(IDXPersonalInfoHistory)).all() + assert len(_personal_info_history_list) == 0 + # # Single Token # Single event logs @@ -280,7 +290,7 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -291,7 +301,7 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -334,6 +344,16 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): assert _personal_info.account_address == user_address_1 assert _personal_info.issuer_address == issuer_address assert _personal_info.personal_info == personal_info_1 + + _personal_info_history_list = db.scalars(select(IDXPersonalInfoHistory)).all() + assert len(_personal_info_history_list) == 1 + _personal_info_history = _personal_info_history_list[0] + assert _personal_info_history.id == 1 + assert _personal_info_history.account_address == user_address_1 + assert _personal_info_history.event_type == PersonalInfoEventType.REGISTER + assert _personal_info_history.issuer_address == issuer_address + assert _personal_info_history.personal_info == personal_info_1 + _idx_personal_info_block_number = db.scalars( select(IDXPersonalInfoBlockNumber).limit(1) ).first() @@ -380,7 +400,7 @@ async def test_normal_2_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -391,7 +411,7 @@ async def test_normal_2_2(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -474,6 +494,22 @@ async def test_normal_2_2(self, processor, db, personal_info_contract): assert _personal_info.account_address == user_address_1 assert _personal_info.issuer_address == issuer_address assert _personal_info.personal_info == personal_info_2 + + _personal_info_history_list = db.scalars(select(IDXPersonalInfoHistory)).all() + assert len(_personal_info_history_list) == 2 + _personal_info_history_1 = _personal_info_history_list[0] + assert _personal_info_history_1.id == 1 + assert _personal_info_history_1.account_address == user_address_1 + assert _personal_info_history_1.event_type == PersonalInfoEventType.REGISTER + assert _personal_info_history_1.issuer_address == issuer_address + assert _personal_info_history_1.personal_info == personal_info_1 + _personal_info_history_2 = _personal_info_history_list[1] + assert _personal_info_history_2.id == 2 + assert _personal_info_history_2.account_address == user_address_1 + assert _personal_info_history_2.event_type == PersonalInfoEventType.MODIFY + assert _personal_info_history_2.issuer_address == issuer_address + assert _personal_info_history_2.personal_info == personal_info_2 + _idx_personal_info_block_number = db.scalars( select(IDXPersonalInfoBlockNumber).limit(1) ).first() @@ -520,7 +556,7 @@ async def test_normal_3(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -531,7 +567,7 @@ async def test_normal_3(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -614,6 +650,24 @@ async def test_normal_3(self, processor, db, personal_info_contract): assert _personal_info.account_address == user_address_1 assert _personal_info.issuer_address == issuer_address assert _personal_info.personal_info == personal_info_2 + + _personal_info_history_list = db.scalars( + select(IDXPersonalInfoHistory).order_by(IDXPersonalInfoHistory.id) + ).all() + assert len(_personal_info_history_list) == 2 + _personal_info_history_1 = _personal_info_history_list[0] + assert _personal_info_history_1.id == 1 + assert _personal_info_history_1.account_address == user_address_1 + assert _personal_info_history_1.event_type == PersonalInfoEventType.REGISTER + assert _personal_info_history_1.issuer_address == issuer_address + assert _personal_info_history_1.personal_info == personal_info_1 + _personal_info_history_2 = _personal_info_history_list[1] + assert _personal_info_history_2.id == 2 + assert _personal_info_history_2.account_address == user_address_1 + assert _personal_info_history_2.event_type == PersonalInfoEventType.MODIFY + assert _personal_info_history_2.issuer_address == issuer_address + assert _personal_info_history_2.personal_info == personal_info_2 + _idx_personal_info_block_number = db.scalars( select(IDXPersonalInfoBlockNumber).limit(1) ).first() @@ -662,6 +716,18 @@ async def test_normal_3(self, processor, db, personal_info_contract): assert _personal_info.account_address == user_address_1 assert _personal_info.issuer_address == issuer_address assert _personal_info.personal_info == personal_info_3 + + _personal_info_history_list = db.scalars( + select(IDXPersonalInfoHistory).order_by(IDXPersonalInfoHistory.id) + ).all() + assert len(_personal_info_history_list) == 3 + _personal_info_history = _personal_info_history_list[2] + assert _personal_info_history.id == 3 + assert _personal_info_history.account_address == user_address_1 + assert _personal_info_history.event_type == PersonalInfoEventType.MODIFY + assert _personal_info_history.issuer_address == issuer_address + assert _personal_info_history.personal_info == personal_info_3 + _idx_personal_info_block_number = db.scalars( select(IDXPersonalInfoBlockNumber).limit(1) ).first() @@ -736,7 +802,7 @@ async def test_normal_4(self, processor, db): token_1.issuer_address = issuer_address_1 token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Issuer2 issues bond token. @@ -753,7 +819,7 @@ async def test_normal_4(self, processor, db): token_2.issuer_address = issuer_address_2 token_2.abi = token_contract2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -845,6 +911,20 @@ async def test_normal_4(self, processor, db): == personal_info_dict[stored_address_order[i]] ) + _personal_info_history_list = db.scalars(select(IDXPersonalInfoHistory)).all() + assert len(_personal_info_history_list) == 2 + + for i in range(2): + _personal_info_history = _personal_info_history_list[i] + assert _personal_info_history.id == i + 1 + assert _personal_info_history.account_address == stored_address_order[i] + assert _personal_info_history.event_type == PersonalInfoEventType.REGISTER + assert _personal_info_history.issuer_address == stored_address_order[i] + assert ( + _personal_info_history.personal_info + == personal_info_dict[stored_address_order[i]] + ) + _idx_personal_info_block_number = db.scalars( select(IDXPersonalInfoBlockNumber).limit(1) ).first() @@ -869,130 +949,6 @@ async def test_normal_5( (LOG.name, logging.DEBUG, "skip process") ) - # - # If DB session fails in phase sinking register/modify events, batch logs exception message. - @pytest.mark.asyncio - async def test_normal_6( - self, - processor: Processor, - db: Session, - personal_info_contract, - caplog: pytest.LogCaptureFixture, - ): - user_1 = config_eth_account("user1") - issuer_address = user_1["address"] - issuer_private_key = decode_keyfile_json( - raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") - ) - issuer_rsa_private_key = user_1["rsa_private_key"] - issuer_rsa_public_key = user_1["rsa_public_key"] - issuer_rsa_passphrase = "password" - user_2 = config_eth_account("user2") - user_address_1 = user_2["address"] - user_private_key_1 = decode_keyfile_json( - raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") - ) - - # Prepare data : Account - account = Account() - account.issuer_address = issuer_address - account.rsa_private_key = issuer_rsa_private_key - account.rsa_public_key = issuer_rsa_public_key - account.rsa_passphrase = E2EEUtils.encrypt(issuer_rsa_passphrase) - account.rsa_status = 3 - db.add(account) - - # Prepare data : Token - token_contract_1 = await deploy_bond_token_contract( - issuer_address, issuer_private_key, personal_info_contract.address - ) - token_address_1 = token_contract_1.address - token_1 = Token() - token_1.type = TokenType.IBET_STRAIGHT_BOND.value - token_1.token_address = token_address_1 - token_1.issuer_address = issuer_address - token_1.abi = token_contract_1.abi - token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 - db.add(token_1) - - # Prepare data : Token(processing token) - token_2 = Token() - token_2.type = TokenType.IBET_STRAIGHT_BOND.value - token_2.token_address = "test1" - token_2.issuer_address = issuer_address - token_2.abi = "abi" - token_2.tx_hash = "tx_hash" - token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 - db.add(token_2) - - db.commit() - - # Register - personal_info_1 = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } - ciphertext = encrypt_personal_info( - personal_info_1, issuer_rsa_public_key, issuer_rsa_passphrase - ) - tx = personal_info_contract.functions.register( - issuer_address, ciphertext.decode("utf-8") - ).build_transaction( - { - "chainId": CHAIN_ID, - "from": user_address_1, - "gas": TX_GAS_LIMIT, - "gasPrice": 0, - } - ) - ContractUtils.send_transaction(tx, user_private_key_1) - - # Modify - personal_info_2 = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": True, - "tax_category": 20, - } - ciphertext = encrypt_personal_info( - personal_info_2, issuer_rsa_public_key, issuer_rsa_passphrase - ) - tx = personal_info_contract.functions.modify( - user_address_1, ciphertext.decode("utf-8") - ).build_transaction( - { - "chainId": CHAIN_ID, - "from": issuer_address, - "gas": TX_GAS_LIMIT, - "gasPrice": 0, - } - ) - ContractUtils.send_transaction(tx, issuer_private_key) - - with patch.object(Session, "add", side_effect=Exception()): - # Then execute processor. - await processor.process() - - assert 2 == caplog.record_tuples.count( - ( - LOG.name, - logging.ERROR, - "An exception occurred during event synchronization", - ) - ) - ########################################################################### # Error Case ########################################################################### @@ -1041,7 +997,7 @@ async def test_error_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() diff --git a/tests/test_batch_indexer_position_bond.py b/tests/batch/test_indexer_position_bond.py similarity index 85% rename from tests/test_batch_indexer_position_bond.py rename to tests/batch/test_indexer_position_bond.py index 5d844726..f5103951 100644 --- a/tests/test_batch_indexer_position_bond.py +++ b/tests/batch/test_indexer_position_bond.py @@ -18,7 +18,7 @@ """ import logging -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from unittest.mock import patch import pytest @@ -53,9 +53,10 @@ from batch.indexer_position_bond import LOG, Processor, main from config import CHAIN_ID, TOKEN_CACHE_TTL, TX_GAS_LIMIT, ZERO_ADDRESS from tests.account_config import config_eth_account -from tests.utils.contract_utils import ( +from tests.contract_utils import ( IbetExchangeContractTestUtils, IbetSecurityTokenContractTestUtils as STContractUtils, + IbetSecurityTokenDVPContractTestUtils as STDVPContractUtils, IbetSecurityTokenEscrowContractTestUtils as STEscrowContractUtils, PersonalInfoContractTestUtils, ) @@ -144,7 +145,7 @@ async def test_normal_1_1( token_1.issuer_address = issuer_address token_1.abi = "abi" token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -155,7 +156,7 @@ async def test_normal_1_1( token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -205,7 +206,7 @@ async def test_normal_1_2( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -215,7 +216,7 @@ async def test_normal_1_2( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -226,7 +227,7 @@ async def test_normal_1_2( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) # Prepare data : BlockNumber @@ -294,7 +295,7 @@ async def test_normal_2_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -304,7 +305,7 @@ async def test_normal_2_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -315,7 +316,7 @@ async def test_normal_2_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -402,7 +403,7 @@ async def test_normal_2_2_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -412,7 +413,7 @@ async def test_normal_2_2_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -423,7 +424,7 @@ async def test_normal_2_2_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -515,7 +516,7 @@ async def test_normal_2_2_2( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -525,7 +526,7 @@ async def test_normal_2_2_2( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -536,7 +537,7 @@ async def test_normal_2_2_2( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -619,7 +620,7 @@ async def test_normal_2_2_3( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -629,7 +630,7 @@ async def test_normal_2_2_3( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -640,7 +641,7 @@ async def test_normal_2_2_3( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -770,7 +771,7 @@ async def test_normal_2_3( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -780,7 +781,7 @@ async def test_normal_2_3( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -791,7 +792,7 @@ async def test_normal_2_3( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -913,7 +914,7 @@ async def test_normal_2_4( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -923,7 +924,7 @@ async def test_normal_2_4( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -934,7 +935,7 @@ async def test_normal_2_4( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1119,7 +1120,7 @@ async def test_normal_2_5( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -1129,7 +1130,7 @@ async def test_normal_2_5( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1140,7 +1141,7 @@ async def test_normal_2_5( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1222,7 +1223,7 @@ async def test_normal_2_6( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -1232,7 +1233,7 @@ async def test_normal_2_6( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1243,7 +1244,7 @@ async def test_normal_2_6( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1329,7 +1330,7 @@ async def test_normal_2_7(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -1339,7 +1340,7 @@ async def test_normal_2_7(self, processor, db, personal_info_contract): token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1350,7 +1351,7 @@ async def test_normal_2_7(self, processor, db, personal_info_contract): token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1469,7 +1470,7 @@ async def test_normal_2_8( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -1479,7 +1480,7 @@ async def test_normal_2_8( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1490,7 +1491,7 @@ async def test_normal_2_8( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1619,7 +1620,7 @@ async def test_normal_2_9_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -1629,7 +1630,7 @@ async def test_normal_2_9_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1640,7 +1641,7 @@ async def test_normal_2_9_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1766,7 +1767,7 @@ async def test_normal_2_9_2( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -1923,7 +1924,7 @@ async def test_normal_2_9_3( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2083,7 +2084,7 @@ async def test_normal_2_9_4( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2243,7 +2244,7 @@ async def test_normal_2_9_5( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2412,7 +2413,7 @@ async def test_normal_2_9_6( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2571,7 +2572,7 @@ async def test_normal_2_10_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -2581,7 +2582,7 @@ async def test_normal_2_10_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -2592,7 +2593,7 @@ async def test_normal_2_10_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -2718,7 +2719,7 @@ async def test_normal_2_10_2( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2876,7 +2877,7 @@ async def test_normal_2_10_3( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2985,6 +2986,634 @@ async def test_normal_2_10_3( assert _idx_position_bond_block_number.id == 1 assert _idx_position_bond_block_number.latest_block_number == block_number + # + # Single Token + # Single event logs + # - IbetSecurityTokenDVP: DeliveryCreated + @pytest.mark.asyncio + async def test_normal_2_11_1( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(share token) + token_2 = Token() + token_2.type = TokenType.IBET_SHARE.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : Token(processing token) + token_3 = Token() + token_3.type = TokenType.IBET_STRAIGHT_BOND.value + token_3.token_address = "test1" + token_3.issuer_address = issuer_address + token_3.abi = "abi" + token_3.tx_hash = "tx_hash" + token_3.token_status = 0 + token_3.version = TokenVersion.V_24_06 + db.add(token_3) + + db.commit() + + # Deposit + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # Before run(consume accumulated events) + await processor.sync_new_logs() + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 1 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 40 + assert _position.exchange_balance == 40 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + + # EscrowCreated + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_contract_1.address, user_address_1, 30, issuer_address, "" + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # Run target process + block_number = web3.eth.block_number + await processor.sync_new_logs() + + # If we query in one session before and after update some record in another session, + # SQLAlchemy will return same result twice. So Expiring all persistent instances within unittest db session. + db.expire_all() + + # Assertion + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 1 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 40 + assert _position.exchange_balance == 40 - 30 + assert _position.exchange_commitment == 30 + assert _position.pending_transfer == 0 + _idx_position_bond_block_number = db.scalars( + select(IDXPositionBondBlockNumber).limit(1) + ).first() + assert _idx_position_bond_block_number.id == 1 + assert _idx_position_bond_block_number.latest_block_number == block_number + + # + # Single Token + # Single event logs + # - IbetSecurityTokenDVP: DeliveryCanceled + @pytest.mark.asyncio + async def test_normal_2_11_2( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_pk_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + user_address_2 = user_3["address"] + user_pk_2 = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Issuer issues bond token. + token_contract = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + transfer_approval_required=False, + ) + token_address_1 = token_contract.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + db.commit() + + # Before run(consume accumulated events) + await processor.sync_new_logs() + + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_1, + user_pk_1, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_2, + user_pk_2, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + issuer_address, + issuer_private_key, + [issuer_address, ""], + ) + + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_1, 30], + ) + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_2, 10], + ) + + # CreateDelivery & CancelDelivery + STContractUtils.transfer( + token_contract.address, + user_address_1, + user_pk_1, + [ibet_security_token_dvp_contract.address, 30], + ) + + STDVPContractUtils.create_delivery( + ibet_security_token_dvp_contract.address, + user_address_1, + user_pk_1, + [token_contract.address, user_address_2, 10, issuer_address, ""], + ) + latest_delivery_id = STDVPContractUtils.get_latest_delivery_id( + ibet_security_token_dvp_contract.address + ) + STDVPContractUtils.cancel_delivery( + ibet_security_token_dvp_contract.address, + user_address_1, + user_pk_1, + [latest_delivery_id], + ) + + # Run target process + block_number = web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 3 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 30 - 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_1) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_1 + assert _position.balance == 0 + assert _position.exchange_balance == 30 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_2) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_2 + assert _position.balance == 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _idx_position_bond_block_number = db.scalars( + select(IDXPositionBondBlockNumber).limit(1) + ).first() + assert _idx_position_bond_block_number.id == 1 + assert _idx_position_bond_block_number.latest_block_number == block_number + + # + # Single Token + # Single event logs + # - IbetSecurityTokenDVP: DeliveryFinished + @pytest.mark.asyncio + async def test_normal_2_11_3( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_pk_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + user_address_2 = user_3["address"] + user_pk_2 = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Issuer issues bond token. + token_contract = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + transfer_approval_required=False, + ) + token_address_1 = token_contract.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + db.commit() + + # Before run(consume accumulated events) + await processor.sync_new_logs() + + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_1, + user_pk_1, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_2, + user_pk_2, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + issuer_address, + issuer_private_key, + [issuer_address, ""], + ) + + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_1, 30], + ) + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_2, 10], + ) + + # CreateEscrow & CancelEscrow + STContractUtils.transfer( + token_contract.address, + user_address_1, + user_pk_1, + [ibet_security_token_dvp_contract.address, 30], + ) + + STDVPContractUtils.create_delivery( + ibet_security_token_dvp_contract.address, + user_address_1, + user_pk_1, + [token_contract.address, user_address_2, 10, issuer_address, ""], + ) + latest_delivery_id = STDVPContractUtils.get_latest_delivery_id( + ibet_security_token_dvp_contract.address + ) + STDVPContractUtils.confirm_delivery( + ibet_security_token_dvp_contract.address, + user_address_2, + user_pk_2, + [latest_delivery_id], + ) + STDVPContractUtils.finish_delivery( + ibet_security_token_dvp_contract.address, + issuer_address, + issuer_private_key, + [latest_delivery_id], + ) + # Run target process + block_number = web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 3 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 30 - 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_1) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_1 + assert _position.balance == 0 + assert _position.exchange_balance == 20 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_2) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_2 + assert _position.balance == 10 + assert _position.exchange_balance == 10 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _idx_position_bond_block_number = db.scalars( + select(IDXPositionBondBlockNumber).limit(1) + ).first() + assert _idx_position_bond_block_number.id == 1 + assert _idx_position_bond_block_number.latest_block_number == block_number + + # + # Single Token + # Single event logs + # - IbetSecurityTokenDVP: DeliveryAborted + @pytest.mark.asyncio + async def test_normal_2_11_4( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_pk_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + user_address_2 = user_3["address"] + user_pk_2 = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Issuer issues bond token. + token_contract = await deploy_bond_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + transfer_approval_required=False, + ) + token_address_1 = token_contract.address + token_1 = Token() + token_1.type = TokenType.IBET_STRAIGHT_BOND.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + db.commit() + + # Before run(consume accumulated events) + await processor.sync_new_logs() + + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_1, + user_pk_1, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_2, + user_pk_2, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + issuer_address, + issuer_private_key, + [issuer_address, ""], + ) + + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_1, 30], + ) + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_2, 10], + ) + + # CreateDelivery & CancelDelivery + STContractUtils.transfer( + token_contract.address, + user_address_1, + user_pk_1, + [ibet_security_token_dvp_contract.address, 30], + ) + + STDVPContractUtils.create_delivery( + ibet_security_token_dvp_contract.address, + user_address_1, + user_pk_1, + [token_contract.address, user_address_2, 10, issuer_address, ""], + ) + latest_delivery_id = STDVPContractUtils.get_latest_delivery_id( + ibet_security_token_dvp_contract.address + ) + STDVPContractUtils.confirm_delivery( + ibet_security_token_dvp_contract.address, + user_address_2, + user_pk_2, + [latest_delivery_id], + ) + STDVPContractUtils.abort_delivery( + ibet_security_token_dvp_contract.address, + issuer_address, + issuer_private_key, + [latest_delivery_id], + ) + + # Run target process + block_number = web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 3 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 30 - 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_1) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_1 + assert _position.balance == 0 + assert _position.exchange_balance == 30 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_2) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_2 + assert _position.balance == 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _idx_position_bond_block_number = db.scalars( + select(IDXPositionBondBlockNumber).limit(1) + ).first() + assert _idx_position_bond_block_number.id == 1 + assert _idx_position_bond_block_number.latest_block_number == block_number + # # Single Token # Multi event logs @@ -3027,7 +3656,7 @@ async def test_normal_3_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -3037,7 +3666,7 @@ async def test_normal_3_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -3048,7 +3677,7 @@ async def test_normal_3_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -3190,7 +3819,7 @@ async def test_normal_3_2( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -3200,7 +3829,7 @@ async def test_normal_3_2( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -3394,7 +4023,7 @@ async def test_normal_3_3( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -3566,7 +4195,7 @@ async def test_normal_3_4( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -3722,7 +4351,7 @@ async def test_normal_4( token_1.issuer_address = issuer_address token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Issuer issues bond token. @@ -3740,7 +4369,7 @@ async def test_normal_4( token_2.issuer_address = issuer_address token_2.abi = token_contract2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -3960,7 +4589,7 @@ async def test_normal_6_1( token_1.issuer_address = issuer_address token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -4001,7 +4630,7 @@ async def test_normal_6_1( token_2.issuer_address = issuer_address token_2.abi = token_contract2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -4056,7 +4685,7 @@ async def test_normal_6_2( token_1.issuer_address = issuer_address token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 token_1.initial_position_synced = True # already synced db.add(token_1) @@ -4110,7 +4739,7 @@ async def test_error_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) token_attr = { @@ -4158,10 +4787,10 @@ async def test_error_1( token_cache = TokenCache() token_cache.token_address = token_address_1 token_cache.attributes = token_attr - token_cache.cached_datetime = datetime.utcnow() - token_cache.expiration_datetime = datetime.utcnow() + timedelta( - seconds=TOKEN_CACHE_TTL - ) + token_cache.cached_datetime = datetime.now(UTC).replace(tzinfo=None) + token_cache.expiration_datetime = datetime.now(UTC).replace( + tzinfo=None + ) + timedelta(seconds=TOKEN_CACHE_TTL) db.add(token_cache) db.commit() diff --git a/tests/test_batch_indexer_position_share.py b/tests/batch/test_indexer_position_share.py similarity index 85% rename from tests/test_batch_indexer_position_share.py rename to tests/batch/test_indexer_position_share.py index 4a0c8375..2bc2627d 100644 --- a/tests/test_batch_indexer_position_share.py +++ b/tests/batch/test_indexer_position_share.py @@ -18,7 +18,7 @@ """ import logging -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from unittest import mock from unittest.mock import patch @@ -48,15 +48,16 @@ TokenType, TokenVersion, ) -from app.utils.contract_utils import AsyncContractUtils, ContractUtils +from app.utils.contract_utils import ContractUtils from app.utils.e2ee_utils import E2EEUtils from app.utils.web3_utils import AsyncWeb3Wrapper, Web3Wrapper from batch.indexer_position_share import LOG, Processor, main from config import CHAIN_ID, TOKEN_CACHE_TTL, TX_GAS_LIMIT, ZERO_ADDRESS from tests.account_config import config_eth_account -from tests.utils.contract_utils import ( +from tests.contract_utils import ( IbetExchangeContractTestUtils, IbetSecurityTokenContractTestUtils as STContractUtils, + IbetSecurityTokenDVPContractTestUtils as STDVPContractUtils, IbetSecurityTokenEscrowContractTestUtils as STEscrowContractUtils, PersonalInfoContractTestUtils, ) @@ -150,7 +151,7 @@ async def test_normal_1_1( token_1.issuer_address = issuer_address token_1.abi = "abi" token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -161,7 +162,7 @@ async def test_normal_1_1( token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -211,7 +212,7 @@ async def test_normal_1_2( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -221,7 +222,7 @@ async def test_normal_1_2( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -232,7 +233,7 @@ async def test_normal_1_2( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) # Prepare data : BlockNumber @@ -300,7 +301,7 @@ async def test_normal_2_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -310,7 +311,7 @@ async def test_normal_2_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -321,7 +322,7 @@ async def test_normal_2_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -408,7 +409,7 @@ async def test_normal_2_2_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -418,7 +419,7 @@ async def test_normal_2_2_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -429,7 +430,7 @@ async def test_normal_2_2_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -521,7 +522,7 @@ async def test_normal_2_2_2( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -531,7 +532,7 @@ async def test_normal_2_2_2( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -542,7 +543,7 @@ async def test_normal_2_2_2( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -625,7 +626,7 @@ async def test_normal_2_2_3( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -635,7 +636,7 @@ async def test_normal_2_2_3( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -646,7 +647,7 @@ async def test_normal_2_2_3( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -776,7 +777,7 @@ async def test_normal_2_3( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -786,7 +787,7 @@ async def test_normal_2_3( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -797,7 +798,7 @@ async def test_normal_2_3( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -919,7 +920,7 @@ async def test_normal_2_4( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -929,7 +930,7 @@ async def test_normal_2_4( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -940,7 +941,7 @@ async def test_normal_2_4( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1125,7 +1126,7 @@ async def test_normal_2_5( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -1135,7 +1136,7 @@ async def test_normal_2_5( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1146,7 +1147,7 @@ async def test_normal_2_5( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1228,7 +1229,7 @@ async def test_normal_2_6( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -1238,7 +1239,7 @@ async def test_normal_2_6( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1249,7 +1250,7 @@ async def test_normal_2_6( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1337,7 +1338,7 @@ async def test_normal_2_7( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -1347,7 +1348,7 @@ async def test_normal_2_7( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1358,7 +1359,7 @@ async def test_normal_2_7( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1477,7 +1478,7 @@ async def test_normal_2_8( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -1487,7 +1488,7 @@ async def test_normal_2_8( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1498,7 +1499,7 @@ async def test_normal_2_8( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1627,7 +1628,7 @@ async def test_normal_2_9_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -1637,7 +1638,7 @@ async def test_normal_2_9_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -1648,7 +1649,7 @@ async def test_normal_2_9_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -1774,7 +1775,7 @@ async def test_normal_2_9_2( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -1931,7 +1932,7 @@ async def test_normal_2_9_3( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2091,7 +2092,7 @@ async def test_normal_2_9_4( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2251,7 +2252,7 @@ async def test_normal_2_9_5( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2420,7 +2421,7 @@ async def test_normal_2_9_6( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2579,7 +2580,7 @@ async def test_normal_2_10_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -2589,7 +2590,7 @@ async def test_normal_2_10_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -2600,7 +2601,7 @@ async def test_normal_2_10_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -2726,7 +2727,7 @@ async def test_normal_2_10_2( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2884,7 +2885,7 @@ async def test_normal_2_10_3( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -2993,6 +2994,634 @@ async def test_normal_2_10_3( assert _idx_position_share_block_number.id == 1 assert _idx_position_share_block_number.latest_block_number == block_number + # + # Single Token + # Single event logs + # - IbetSecurityTokenDVP: DeliveryCreated + @pytest.mark.asyncio + async def test_normal_2_11_1( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Prepare data : Token + token_contract_1 = await deploy_share_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + ibet_security_token_dvp_contract.address, + ) + token_address_1 = token_contract_1.address + token_1 = Token() + token_1.type = TokenType.IBET_SHARE.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract_1.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + # Prepare data : Token(bond token) + token_2 = Token() + token_2.type = TokenType.IBET_STRAIGHT_BOND.value + token_2.token_address = "test1" + token_2.issuer_address = issuer_address + token_2.abi = "abi" + token_2.tx_hash = "tx_hash" + token_2.version = TokenVersion.V_24_06 + db.add(token_2) + + # Prepare data : Token(processing token) + token_3 = Token() + token_3.type = TokenType.IBET_SHARE.value + token_3.token_address = "test1" + token_3.issuer_address = issuer_address + token_3.abi = "abi" + token_3.tx_hash = "tx_hash" + token_3.token_status = 0 + token_3.version = TokenVersion.V_24_06 + db.add(token_3) + + db.commit() + + # Deposit + tx = token_contract_1.functions.transferFrom( + issuer_address, ibet_security_token_dvp_contract.address, 40 + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # Before run(consume accumulated events) + await processor.sync_new_logs() + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 1 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 40 + assert _position.exchange_balance == 40 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + + # EscrowCreated + tx = ibet_security_token_dvp_contract.functions.createDelivery( + token_contract_1.address, user_address_1, 30, issuer_address, "" + ).build_transaction( + { + "chainId": CHAIN_ID, + "from": issuer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, issuer_private_key) + + # Run target process + block_number = web3.eth.block_number + await processor.sync_new_logs() + + # If we query in one session before and after update some record in another session, + # SQLAlchemy will return same result twice. So Expiring all persistent instances within unittest db session. + db.expire_all() + + # Assertion + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 1 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 40 + assert _position.exchange_balance == 40 - 30 + assert _position.exchange_commitment == 30 + assert _position.pending_transfer == 0 + _idx_position_share_block_number = db.scalars( + select(IDXPositionShareBlockNumber).limit(1) + ).first() + assert _idx_position_share_block_number.id == 1 + assert _idx_position_share_block_number.latest_block_number == block_number + + # + # Single Token + # Single event logs + # - IbetSecurityTokenDVP: DeliveryCanceled + @pytest.mark.asyncio + async def test_normal_2_11_2( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_pk_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + user_address_2 = user_3["address"] + user_pk_2 = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Issuer issues share token. + token_contract = await deploy_share_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + transfer_approval_required=False, + ) + token_address_1 = token_contract.address + token_1 = Token() + token_1.type = TokenType.IBET_SHARE.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + db.commit() + + # Before run(consume accumulated events) + await processor.sync_new_logs() + + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_1, + user_pk_1, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_2, + user_pk_2, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + issuer_address, + issuer_private_key, + [issuer_address, ""], + ) + + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_1, 30], + ) + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_2, 10], + ) + + # CreateDelivery & CancelDelivery + STContractUtils.transfer( + token_contract.address, + user_address_1, + user_pk_1, + [ibet_security_token_dvp_contract.address, 30], + ) + + STDVPContractUtils.create_delivery( + ibet_security_token_dvp_contract.address, + user_address_1, + user_pk_1, + [token_contract.address, user_address_2, 10, issuer_address, ""], + ) + latest_delivery_id = STDVPContractUtils.get_latest_delivery_id( + ibet_security_token_dvp_contract.address + ) + STDVPContractUtils.cancel_delivery( + ibet_security_token_dvp_contract.address, + user_address_1, + user_pk_1, + [latest_delivery_id], + ) + + # Run target process + block_number = web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 3 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 30 - 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_1) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_1 + assert _position.balance == 0 + assert _position.exchange_balance == 30 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_2) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_2 + assert _position.balance == 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _idx_position_share_block_number = db.scalars( + select(IDXPositionShareBlockNumber).limit(1) + ).first() + assert _idx_position_share_block_number.id == 1 + assert _idx_position_share_block_number.latest_block_number == block_number + + # + # Single Token + # Single event logs + # - IbetSecurityTokenDVP: DeliveryFinished + @pytest.mark.asyncio + async def test_normal_2_11_3( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_pk_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + user_address_2 = user_3["address"] + user_pk_2 = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Issuer issues share token. + token_contract = await deploy_share_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + transfer_approval_required=False, + ) + token_address_1 = token_contract.address + token_1 = Token() + token_1.type = TokenType.IBET_SHARE.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + db.commit() + + # Before run(consume accumulated events) + await processor.sync_new_logs() + + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_1, + user_pk_1, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_2, + user_pk_2, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + issuer_address, + issuer_private_key, + [issuer_address, ""], + ) + + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_1, 30], + ) + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_2, 10], + ) + + # CreateEscrow & CancelEscrow + STContractUtils.transfer( + token_contract.address, + user_address_1, + user_pk_1, + [ibet_security_token_dvp_contract.address, 30], + ) + + STDVPContractUtils.create_delivery( + ibet_security_token_dvp_contract.address, + user_address_1, + user_pk_1, + [token_contract.address, user_address_2, 10, issuer_address, ""], + ) + latest_delivery_id = STDVPContractUtils.get_latest_delivery_id( + ibet_security_token_dvp_contract.address + ) + STDVPContractUtils.confirm_delivery( + ibet_security_token_dvp_contract.address, + user_address_2, + user_pk_2, + [latest_delivery_id], + ) + STDVPContractUtils.finish_delivery( + ibet_security_token_dvp_contract.address, + issuer_address, + issuer_private_key, + [latest_delivery_id], + ) + # Run target process + block_number = web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 3 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 30 - 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_1) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_1 + assert _position.balance == 0 + assert _position.exchange_balance == 20 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_2) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_2 + assert _position.balance == 10 + assert _position.exchange_balance == 10 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _idx_position_share_block_number = db.scalars( + select(IDXPositionShareBlockNumber).limit(1) + ).first() + assert _idx_position_share_block_number.id == 1 + assert _idx_position_share_block_number.latest_block_number == block_number + + # + # Single Token + # Single event logs + # - IbetSecurityTokenDVP: DeliveryAborted + @pytest.mark.asyncio + async def test_normal_2_11_4( + self, + processor: Processor, + db: Session, + personal_info_contract, + ibet_security_token_dvp_contract, + ): + user_1 = config_eth_account("user1") + issuer_address = user_1["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + user_2 = config_eth_account("user2") + user_address_1 = user_2["address"] + user_pk_1 = decode_keyfile_json( + raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") + ) + user_3 = config_eth_account("user3") + user_address_2 = user_3["address"] + user_pk_2 = decode_keyfile_json( + raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") + ) + + # Prepare data : Account + account = Account() + account.issuer_address = issuer_address + account.keyfile = user_1["keyfile_json"] + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + # Issuer issues share token. + token_contract = await deploy_share_token_contract( + issuer_address, + issuer_private_key, + personal_info_contract.address, + tradable_exchange_contract_address=ibet_security_token_dvp_contract.address, + transfer_approval_required=False, + ) + token_address_1 = token_contract.address + token_1 = Token() + token_1.type = TokenType.IBET_SHARE.value + token_1.token_address = token_address_1 + token_1.issuer_address = issuer_address + token_1.abi = token_contract.abi + token_1.tx_hash = "tx_hash" + token_1.version = TokenVersion.V_24_06 + db.add(token_1) + + db.commit() + + # Before run(consume accumulated events) + await processor.sync_new_logs() + + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_1, + user_pk_1, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + user_address_2, + user_pk_2, + [issuer_address, ""], + ) + PersonalInfoContractTestUtils.register( + personal_info_contract.address, + issuer_address, + issuer_private_key, + [issuer_address, ""], + ) + + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_1, 30], + ) + STContractUtils.transfer( + token_contract.address, + issuer_address, + issuer_private_key, + [user_address_2, 10], + ) + + # CreateDelivery & CancelDelivery + STContractUtils.transfer( + token_contract.address, + user_address_1, + user_pk_1, + [ibet_security_token_dvp_contract.address, 30], + ) + + STDVPContractUtils.create_delivery( + ibet_security_token_dvp_contract.address, + user_address_1, + user_pk_1, + [token_contract.address, user_address_2, 10, issuer_address, ""], + ) + latest_delivery_id = STDVPContractUtils.get_latest_delivery_id( + ibet_security_token_dvp_contract.address + ) + STDVPContractUtils.confirm_delivery( + ibet_security_token_dvp_contract.address, + user_address_2, + user_pk_2, + [latest_delivery_id], + ) + STDVPContractUtils.abort_delivery( + ibet_security_token_dvp_contract.address, + issuer_address, + issuer_private_key, + [latest_delivery_id], + ) + + # Run target process + block_number = web3.eth.block_number + await processor.sync_new_logs() + + # Assertion + _position_list = db.scalars(select(IDXPosition)).all() + assert len(_position_list) == 3 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == issuer_address) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == issuer_address + assert _position.balance == 100 - 30 - 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_1) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_1 + assert _position.balance == 0 + assert _position.exchange_balance == 30 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _position = db.scalars( + select(IDXPosition) + .where(IDXPosition.account_address == user_address_2) + .limit(1) + ).first() + assert _position.token_address == token_address_1 + assert _position.account_address == user_address_2 + assert _position.balance == 10 + assert _position.exchange_balance == 0 + assert _position.exchange_commitment == 0 + assert _position.pending_transfer == 0 + _idx_position_share_block_number = db.scalars( + select(IDXPositionShareBlockNumber).limit(1) + ).first() + assert _idx_position_share_block_number.id == 1 + assert _idx_position_share_block_number.latest_block_number == block_number + # # Single Token # Multi event logs @@ -3035,7 +3664,7 @@ async def test_normal_3_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(bond token) @@ -3045,7 +3674,7 @@ async def test_normal_3_1( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -3056,7 +3685,7 @@ async def test_normal_3_1( token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_22_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() @@ -3198,7 +3827,7 @@ async def test_normal_3_2( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(share token) @@ -3208,7 +3837,7 @@ async def test_normal_3_2( token_2.issuer_address = issuer_address token_2.abi = "abi" token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -3402,7 +4031,7 @@ async def test_normal_3_3( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -3574,7 +4203,7 @@ async def test_normal_3_4( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -3730,7 +4359,7 @@ async def test_normal_4( token_1.issuer_address = issuer_address token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Issuer issues share token. @@ -3748,7 +4377,7 @@ async def test_normal_4( token_2.issuer_address = issuer_address token_2.abi = token_contract2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -3969,7 +4598,7 @@ async def test_normal_6_1( token_1.issuer_address = issuer_address token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -4010,7 +4639,7 @@ async def test_normal_6_1( token_2.issuer_address = issuer_address token_2.abi = token_contract2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -4065,7 +4694,7 @@ async def test_normal_6_2( token_1.issuer_address = issuer_address token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 token_1.initial_position_synced = True # already synced db.add(token_1) @@ -4121,7 +4750,7 @@ async def test_error_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : TokenCache @@ -4151,10 +4780,10 @@ async def test_error_1( token_cache = TokenCache() token_cache.token_address = token_address_1 token_cache.attributes = token_attr - token_cache.cached_datetime = datetime.utcnow() - token_cache.expiration_datetime = datetime.utcnow() + timedelta( - seconds=TOKEN_CACHE_TTL - ) + token_cache.cached_datetime = datetime.now(UTC).replace(tzinfo=None) + token_cache.expiration_datetime = datetime.now(UTC).replace( + tzinfo=None + ) + timedelta(seconds=TOKEN_CACHE_TTL) db.add(token_cache) db.commit() diff --git a/tests/test_batch_indexer_token_cache.py b/tests/batch/test_indexer_token_cache.py similarity index 96% rename from tests/test_batch_indexer_token_cache.py rename to tests/batch/test_indexer_token_cache.py index a612b88e..5c37f17a 100644 --- a/tests/test_batch_indexer_token_cache.py +++ b/tests/batch/test_indexer_token_cache.py @@ -18,10 +18,9 @@ """ import logging -import time -from datetime import datetime +from datetime import UTC, datetime from unittest import mock -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, patch import pytest from eth_keyfile import decode_keyfile_json @@ -162,7 +161,7 @@ async def test_normal_1_1(self, processor, db, personal_info_contract): token_1.abi = "abi" token_1.tx_hash = "tx_hash" token_1.token_status = 0 - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -208,7 +207,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token @@ -222,7 +221,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_2.issuer_address = issuer_address token_2.abi = token_contract_2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : Token(processing token) @@ -233,12 +232,12 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_3.abi = "abi" token_3.tx_hash = "tx_hash" token_3.token_status = 0 - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) db.commit() - before_cache_time = datetime.utcnow() + before_cache_time = datetime.now(UTC).replace(tzinfo=None) sleep_mock = AsyncMock() sleep_mock.return_value = 0 @@ -268,6 +267,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): "privacy_policy": "", "status": True, "personal_info_contract_address": personal_info_contract.address, + "require_personal_info_registered": True, "transferable": True, "is_offering": False, "transfer_approval_required": False, @@ -304,6 +304,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): "memo": "", "name": "token.name", "personal_info_contract_address": personal_info_contract.address, + "require_personal_info_registered": True, "principal_value": 30, "privacy_policy": "", "status": True, @@ -353,7 +354,7 @@ async def test_error_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() diff --git a/tests/test_batch_indexer_token_holders.py b/tests/batch/test_indexer_token_holders.py similarity index 99% rename from tests/test_batch_indexer_token_holders.py rename to tests/batch/test_indexer_token_holders.py index 1826f415..9a27c225 100644 --- a/tests/test_batch_indexer_token_holders.py +++ b/tests/batch/test_indexer_token_holders.py @@ -49,7 +49,7 @@ from batch.indexer_token_holders import LOG, Processor, main from config import ZERO_ADDRESS from tests.account_config import config_eth_account -from tests.utils.contract_utils import ( +from tests.contract_utils import ( IbetExchangeContractTestUtils, IbetSecurityTokenContractTestUtils as STContractUtils, IbetSecurityTokenEscrowContractTestUtils as STEscrowContractUtils, @@ -249,7 +249,7 @@ async def test_normal_1( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -624,7 +624,7 @@ async def test_normal_2( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -846,7 +846,7 @@ async def test_normal_3( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -1037,7 +1037,7 @@ async def test_normal_4( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -1426,7 +1426,7 @@ async def test_normal_5( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -1648,7 +1648,7 @@ async def test_normal_6( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -1843,7 +1843,7 @@ async def test_normal_7( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -1960,7 +1960,7 @@ async def test_normal_8( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -2145,7 +2145,7 @@ async def test_normal_9( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( @@ -2298,7 +2298,7 @@ async def test_normal_10( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Insert collection record with above token and checkpoint block number @@ -2475,7 +2475,7 @@ async def test_error_3( token_1.issuer_address = issuer_address token_1.abi = token_contract.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) PersonalInfoContractTestUtils.register( diff --git a/tests/test_batch_indexer_transfer.py b/tests/batch/test_indexer_transfer.py similarity index 94% rename from tests/test_batch_indexer_transfer.py rename to tests/batch/test_indexer_transfer.py index 5b642f93..fc83f781 100644 --- a/tests/test_batch_indexer_transfer.py +++ b/tests/batch/test_indexer_transfer.py @@ -19,7 +19,7 @@ import json import logging -from datetime import datetime +from datetime import UTC, datetime from unittest import mock from unittest.mock import patch @@ -53,7 +53,7 @@ from batch.indexer_transfer import LOG, Processor, main from config import CHAIN_ID, TX_GAS_LIMIT from tests.account_config import config_eth_account -from tests.utils.contract_utils import PersonalInfoContractTestUtils +from tests.contract_utils import PersonalInfoContractTestUtils web3 = Web3Wrapper() async_web3 = AsyncWeb3Wrapper() @@ -173,7 +173,7 @@ async def test_normal_1_1(self, processor, db, personal_info_contract): token_1.abi = "abi" token_1.tx_hash = "tx_hash" token_1.token_status = 0 - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -214,7 +214,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -225,7 +225,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : BlockNumber @@ -288,7 +288,7 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -299,7 +299,7 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -364,9 +364,9 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _transfer = _transfer_list[1] assert _transfer.id == 2 @@ -378,9 +378,9 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.UNLOCK.value assert _transfer.data == {"message": "unlock"} block = web3.eth.get_block(tx_receipt_2["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _idx_transfer_block_number = db.scalars( select(IDXTransferBlockNumber).limit(1) @@ -424,7 +424,7 @@ async def test_normal_2_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -435,7 +435,7 @@ async def test_normal_2_2(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -522,7 +522,7 @@ async def test_normal_3_1(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -533,7 +533,7 @@ async def test_normal_3_1(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -638,9 +638,9 @@ async def test_normal_3_1(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _transfer = _transfer_list[1] assert _transfer.id == 2 @@ -652,9 +652,9 @@ async def test_normal_3_1(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None block = web3.eth.get_block(tx_receipt_2["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _transfer = _transfer_list[2] assert _transfer.id == 3 @@ -666,9 +666,9 @@ async def test_normal_3_1(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.UNLOCK.value assert _transfer.data == {"message": "unlock"} block = web3.eth.get_block(tx_receipt_3["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _transfer = _transfer_list[3] assert _transfer.id == 4 @@ -680,9 +680,9 @@ async def test_normal_3_1(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.UNLOCK.value assert _transfer.data == {"message": "unlock"} block = web3.eth.get_block(tx_receipt_4["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _idx_transfer_block_number = db.scalars( select(IDXTransferBlockNumber).limit(1) @@ -740,7 +740,7 @@ async def test_normal_3_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -828,9 +828,9 @@ async def test_normal_3_2(self, processor, db, personal_info_contract): assert _transfer.amount == value_list1[i] assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) block = web3.eth.get_block(tx_receipt_2["blockNumber"]) for i in range(0, 4): @@ -843,9 +843,9 @@ async def test_normal_3_2(self, processor, db, personal_info_contract): assert _transfer.amount == value_list2[i] assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _idx_transfer_block_number = db.scalars( select(IDXTransferBlockNumber).limit(1) @@ -885,7 +885,7 @@ async def test_normal_4(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token2 @@ -900,7 +900,7 @@ async def test_normal_4(self, processor, db, personal_info_contract): token_2.issuer_address = issuer_address token_2.abi = token_contract_2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -970,9 +970,9 @@ async def test_normal_4(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _transfer = _transfer_list[1] assert _transfer.id == 2 assert _transfer.transaction_hash == tx_hash_2 @@ -983,9 +983,9 @@ async def test_normal_4(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None block = web3.eth.get_block(tx_receipt_2["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _transfer = _transfer_list[2] assert _transfer.id == 3 @@ -997,9 +997,9 @@ async def test_normal_4(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None block = web3.eth.get_block(tx_receipt_3["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _transfer = _transfer_list[3] assert _transfer.id == 4 @@ -1011,9 +1011,9 @@ async def test_normal_4(self, processor, db, personal_info_contract): assert _transfer.source_event == IDXTransferSourceEventType.TRANSFER.value assert _transfer.data is None block = web3.eth.get_block(tx_receipt_4["blockNumber"]) - assert _transfer.block_timestamp == datetime.utcfromtimestamp( - block["timestamp"] - ) + assert _transfer.block_timestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) _idx_transfer_block_number = db.scalars( select(IDXTransferBlockNumber).limit(1) @@ -1079,7 +1079,7 @@ async def test_normal_6( token_1.issuer_address = issuer_address token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -1105,7 +1105,7 @@ async def test_normal_6( token_2.issuer_address = issuer_address token_2.abi = token_contract2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -1155,7 +1155,7 @@ async def test_error_1( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() diff --git a/tests/test_batch_indexer_transfer_approval.py b/tests/batch/test_indexer_transfer_approval.py similarity index 87% rename from tests/test_batch_indexer_transfer_approval.py rename to tests/batch/test_indexer_transfer_approval.py index d90704ec..d2039461 100644 --- a/tests/test_batch_indexer_transfer_approval.py +++ b/tests/batch/test_indexer_transfer_approval.py @@ -18,7 +18,7 @@ """ import logging -from datetime import datetime +from datetime import UTC, datetime from unittest import mock from unittest.mock import patch from uuid import UUID @@ -55,7 +55,7 @@ from batch.indexer_transfer_approval import LOG, Processor, main from config import CHAIN_ID, TX_GAS_LIMIT from tests.account_config import config_eth_account -from tests.utils.contract_utils import ( +from tests.contract_utils import ( IbetSecurityTokenContractTestUtils as STContractUtils, IbetSecurityTokenEscrowContractTestUtils as STEscrowContractUtils, PersonalInfoContractTestUtils, @@ -177,7 +177,7 @@ async def test_normal_1_1(self, processor, db, personal_info_contract): token_1.abi = "abi" token_1.tx_hash = "tx_hash" token_1.token_status = 0 - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -224,7 +224,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -235,7 +235,7 @@ async def test_normal_1_2(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : BlockNumber @@ -300,7 +300,7 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -311,7 +311,7 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : BlockNumber @@ -364,10 +364,9 @@ async def test_normal_2_1(self, processor, db, personal_info_contract): assert _transfer_approval.amount == 30 assert _transfer_approval.application_datetime is None block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert ( - _transfer_approval.application_blocktimestamp - == datetime.utcfromtimestamp(block["timestamp"]) - ) + assert _transfer_approval.application_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.approval_datetime is None assert _transfer_approval.approval_blocktimestamp is None assert _transfer_approval.cancellation_blocktimestamp is None @@ -434,7 +433,7 @@ async def test_normal_2_2_1(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BlockNumber @@ -501,16 +500,14 @@ async def test_normal_2_2_1(self, processor, db, personal_info_contract): assert _transfer_approval.to_address == issuer_address assert _transfer_approval.amount == 30 assert _transfer_approval.application_datetime is None - assert ( - _transfer_approval.application_blocktimestamp - == datetime.utcfromtimestamp(block_2["timestamp"]) - ) + assert _transfer_approval.application_blocktimestamp == datetime.fromtimestamp( + block_2["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.approval_datetime is None assert _transfer_approval.approval_blocktimestamp is None - assert ( - _transfer_approval.cancellation_blocktimestamp - == datetime.utcfromtimestamp(block_3["timestamp"]) - ) + assert _transfer_approval.cancellation_blocktimestamp == datetime.fromtimestamp( + block_3["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.cancelled is True assert _transfer_approval.transfer_approved is None @@ -574,7 +571,7 @@ async def test_normal_2_2_2(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BlockNumber @@ -641,16 +638,14 @@ async def test_normal_2_2_2(self, processor, db, personal_info_contract): assert _transfer_approval.to_address == issuer_address assert _transfer_approval.amount == 30 assert _transfer_approval.application_datetime is None - assert ( - _transfer_approval.application_blocktimestamp - == datetime.utcfromtimestamp(block_2["timestamp"]) - ) + assert _transfer_approval.application_blocktimestamp == datetime.fromtimestamp( + block_2["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.approval_datetime is None assert _transfer_approval.approval_blocktimestamp is None - assert ( - _transfer_approval.cancellation_blocktimestamp - == datetime.utcfromtimestamp(block_3["timestamp"]) - ) + assert _transfer_approval.cancellation_blocktimestamp == datetime.fromtimestamp( + block_3["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.cancelled is True assert _transfer_approval.transfer_approved is None @@ -713,7 +708,7 @@ async def test_normal_2_3(self, processor, db, personal_info_contract): token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BlockNumber @@ -751,7 +746,7 @@ async def test_normal_2_3(self, processor, db, personal_info_contract): block_1 = web3.eth.get_block(tx_receipt_1["blockNumber"]) # ApproveTransfer from issuer - now = datetime.utcnow() + now = datetime.now(UTC).replace(tzinfo=None) tx = token_contract_1.functions.approveTransfer( 0, str(now.timestamp()) ).build_transaction( @@ -781,15 +776,14 @@ async def test_normal_2_3(self, processor, db, personal_info_contract): assert _transfer_approval.to_address == issuer_address assert _transfer_approval.amount == 30 assert _transfer_approval.application_datetime is None - assert ( - _transfer_approval.application_blocktimestamp - == datetime.utcfromtimestamp(block_1["timestamp"]) - ) + assert _transfer_approval.application_blocktimestamp == datetime.fromtimestamp( + block_1["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.approval_datetime == now block_2 = web3.eth.get_block(tx_receipt_2["blockNumber"]) - assert _transfer_approval.approval_blocktimestamp == datetime.utcfromtimestamp( - block_2["timestamp"] - ) + assert _transfer_approval.approval_blocktimestamp == datetime.fromtimestamp( + block_2["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.cancellation_blocktimestamp is None assert _transfer_approval.cancelled is None assert _transfer_approval.transfer_approved is True @@ -858,7 +852,7 @@ async def test_normal_2_4( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BlockNumber @@ -894,7 +888,7 @@ async def test_normal_2_4( ContractUtils.send_transaction(tx, user_private_key_1) # ApplyForTransfer - now = datetime.utcnow() + now = datetime.now(UTC).replace(tzinfo=None) tx = ibet_security_token_escrow_contract.functions.createEscrow( token_address_1, user_address_2, @@ -932,10 +926,9 @@ async def test_normal_2_4( assert _transfer_approval.amount == 30 assert _transfer_approval.application_datetime == now block = web3.eth.get_block(tx_receipt_1["blockNumber"]) - assert ( - _transfer_approval.application_blocktimestamp - == datetime.utcfromtimestamp(block["timestamp"]) - ) + assert _transfer_approval.application_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.approval_datetime is None assert _transfer_approval.approval_blocktimestamp is None assert _transfer_approval.cancellation_blocktimestamp is None @@ -1006,7 +999,7 @@ async def test_normal_2_5( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : Token(processing token) @@ -1017,7 +1010,7 @@ async def test_normal_2_5( token_2.abi = "abi" token_2.tx_hash = "tx_hash" token_2.token_status = 0 - token_2.version = TokenVersion.V_23_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) # Prepare data : BlockNumber @@ -1099,16 +1092,14 @@ async def test_normal_2_5( assert _transfer_approval.to_address == user_address_2 assert _transfer_approval.amount == 30 assert _transfer_approval.application_datetime is None - assert ( - _transfer_approval.application_blocktimestamp - == datetime.utcfromtimestamp(block_1["timestamp"]) - ) + assert _transfer_approval.application_blocktimestamp == datetime.fromtimestamp( + block_1["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.approval_datetime is None assert _transfer_approval.approval_blocktimestamp is None - assert ( - _transfer_approval.cancellation_blocktimestamp - == datetime.utcfromtimestamp(block_2["timestamp"]) - ) + assert _transfer_approval.cancellation_blocktimestamp == datetime.fromtimestamp( + block_2["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.cancelled is True assert _transfer_approval.transfer_approved is None @@ -1177,7 +1168,7 @@ async def test_normal_2_6( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BlockNumber @@ -1258,10 +1249,9 @@ async def test_normal_2_6( assert _transfer_approval.to_address == user_address_2 assert _transfer_approval.amount == 30 assert _transfer_approval.application_datetime is None - assert ( - _transfer_approval.application_blocktimestamp - == datetime.utcfromtimestamp(block_1["timestamp"]) - ) + assert _transfer_approval.application_blocktimestamp == datetime.fromtimestamp( + block_1["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.approval_datetime is None assert _transfer_approval.approval_blocktimestamp is None assert _transfer_approval.cancellation_blocktimestamp is None @@ -1332,7 +1322,7 @@ async def test_normal_2_7( token_1.issuer_address = issuer_address token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BlockNumber @@ -1427,14 +1417,13 @@ async def test_normal_2_7( assert _transfer_approval.to_address == user_address_2 assert _transfer_approval.amount == 30 assert _transfer_approval.application_datetime is None - assert ( - _transfer_approval.application_blocktimestamp - == datetime.utcfromtimestamp(block["timestamp"]) - ) + assert _transfer_approval.application_blocktimestamp == datetime.fromtimestamp( + block["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.approval_datetime is None - assert _transfer_approval.approval_blocktimestamp == datetime.utcfromtimestamp( - block_2["timestamp"] - ) + assert _transfer_approval.approval_blocktimestamp == datetime.fromtimestamp( + block_2["timestamp"], UTC + ).replace(tzinfo=None) assert _transfer_approval.cancellation_blocktimestamp is None assert _transfer_approval.cancelled is None assert _transfer_approval.transfer_approved is True @@ -1480,182 +1469,9 @@ async def test_normal_3( ) # - # If DB session fails in sinking phase each event, batch outputs a log "exception occurred". - @pytest.mark.asyncio - async def test_normal_4( - self, - processor, - db, - personal_info_contract, - ibet_security_token_escrow_contract, - caplog: pytest.LogCaptureFixture, - ): - user_1 = config_eth_account("user1") - issuer_address = user_1["address"] - issuer_private_key = decode_keyfile_json( - raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") - ) - user_2 = config_eth_account("user2") - user_address_1 = user_2["address"] - user_pk_1 = decode_keyfile_json( - raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") - ) - user_3 = config_eth_account("user3") - user_address_2 = user_3["address"] - user_pk_2 = decode_keyfile_json( - raw_keyfile_json=user_3["keyfile_json"], password="password".encode("utf-8") - ) - - # Prepare data : Account - account = Account() - account.issuer_address = issuer_address - account.keyfile = user_1["keyfile_json"] - account.eoa_password = E2EEUtils.encrypt("password") - db.add(account) - - # Prepare data : Token - token_contract = await deploy_bond_token_contract( - issuer_address, - issuer_private_key, - personal_info_contract.address, - tradable_exchange_contract_address=ibet_security_token_escrow_contract.address, - transfer_approval_required=True, - ) - token_address = token_contract.address - token = Token() - token.type = TokenType.IBET_STRAIGHT_BOND.value - token.token_address = token_address - token.issuer_address = issuer_address - token.abi = token_contract.abi - token.tx_hash = "tx_hash" - token.version = TokenVersion.V_23_12 - db.add(token) - db.commit() - - PersonalInfoContractTestUtils.register( - personal_info_contract.address, - user_address_1, - user_pk_1, - [issuer_address, ""], - ) - PersonalInfoContractTestUtils.register( - personal_info_contract.address, - user_address_2, - user_pk_2, - [issuer_address, ""], - ) - PersonalInfoContractTestUtils.register( - personal_info_contract.address, - issuer_address, - issuer_private_key, - [issuer_address, ""], - ) - PersonalInfoContractTestUtils.register( - personal_info_contract.address, - issuer_address, - issuer_private_key, - [ibet_security_token_escrow_contract.address, ""], - ) - - STContractUtils.set_transfer_approve_required( - token_contract.address, issuer_address, issuer_private_key, [True] - ) - STContractUtils.apply_for_transfer( - token_contract.address, - issuer_address, - issuer_private_key, - [user_address_1, 10, "to user1#1"], - ) - STContractUtils.apply_for_transfer( - token_contract.address, - issuer_address, - issuer_private_key, - [user_address_1, 20, "to user1#2"], - ) - STContractUtils.apply_for_transfer( - token_contract.address, - issuer_address, - issuer_private_key, - [user_address_2, 10, "to user2#1"], - ) - - STContractUtils.cancel_transfer( - token_contract.address, - issuer_address, - issuer_private_key, - [0, "to user1#1"], - ) - STContractUtils.approve_transfer( - token_contract.address, - issuer_address, - issuer_private_key, - [1, "to user1#2"], - ) - STContractUtils.approve_transfer( - token_contract.address, - issuer_address, - issuer_private_key, - [2, "to user2#1"], - ) - - STContractUtils.set_transfer_approve_required( - token_contract.address, issuer_address, issuer_private_key, [False] - ) - STContractUtils.transfer( - token_contract.address, - user_address_1, - user_pk_1, - [ibet_security_token_escrow_contract.address, 20], - ) - STContractUtils.set_transfer_approve_required( - token_contract.address, issuer_address, issuer_private_key, [True] - ) - - STEscrowContractUtils.create_escrow( - ibet_security_token_escrow_contract.address, - user_address_1, - user_pk_1, - [token_contract.address, user_address_2, 10, issuer_address, "", ""], - ) - latest_security_escrow_id = STEscrowContractUtils.get_latest_escrow_id( - ibet_security_token_escrow_contract.address - ) - - STEscrowContractUtils.finish_escrow( - ibet_security_token_escrow_contract.address, - issuer_address, - issuer_private_key, - [latest_security_escrow_id], - ) - STEscrowContractUtils.approve_transfer( - ibet_security_token_escrow_contract.address, - issuer_address, - issuer_private_key, - [latest_security_escrow_id, ""], - ) - - with caplog.at_level(logging.ERROR, LOG.name), patch.object( - Session, "add", side_effect=Exception() - ): - # Then execute processor. - await processor.sync_new_logs() - - # Error occurs in events with exception of Escrow. - assert ( - caplog.record_tuples.count( - ( - LOG.name, - logging.ERROR, - "An exception occurred during event synchronization", - ) - ) - == 3 - ) - - # # Newly tokens added @pytest.mark.asyncio - async def test_normal_5( + async def test_normal_4( self, processor: Processor, db: Session, @@ -1691,7 +1507,7 @@ async def test_normal_5( token_1.issuer_address = issuer_address token_1.abi = token_contract1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -1718,7 +1534,7 @@ async def test_normal_5( token_2.issuer_address = issuer_address token_2.abi = token_contract2.abi token_2.tx_hash = "tx_hash" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) db.commit() @@ -1769,7 +1585,7 @@ async def test_error_1( token.issuer_address = issuer_address token.abi = token_contract.abi token.tx_hash = "tx_hash" - token.version = TokenVersion.V_23_12 + token.version = TokenVersion.V_24_06 db.add(token) db.commit() diff --git a/tests/test_batch_processor_batch_issue_redeem.py b/tests/batch/test_processor_batch_issue_redeem.py similarity index 100% rename from tests/test_batch_processor_batch_issue_redeem.py rename to tests/batch/test_processor_batch_issue_redeem.py diff --git a/tests/test_batch_processor_bulk_transfer.py b/tests/batch/test_processor_bulk_transfer.py similarity index 100% rename from tests/test_batch_processor_bulk_transfer.py rename to tests/batch/test_processor_bulk_transfer.py diff --git a/tests/test_batch_processor_create_utxo.py b/tests/batch/test_processor_create_utxo.py similarity index 98% rename from tests/test_batch_processor_create_utxo.py rename to tests/batch/test_processor_create_utxo.py index 8dd58620..bc366864 100644 --- a/tests/test_batch_processor_create_utxo.py +++ b/tests/batch/test_processor_create_utxo.py @@ -26,7 +26,7 @@ from eth_keyfile import decode_keyfile_json from sqlalchemy import select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.model.blockchain import IbetShareContract, IbetStraightBondContract from app.model.blockchain.tx_params.ibet_share import ( @@ -50,14 +50,14 @@ from batch.processor_create_utxo import Processor from config import CHAIN_ID, TX_GAS_LIMIT, WEB3_HTTP_PROVIDER from tests.account_config import config_eth_account -from tests.utils.contract_utils import ( +from tests.contract_utils import ( IbetExchangeContractTestUtils, IbetSecurityTokenContractTestUtils as STContractUtils, PersonalInfoContractTestUtils, ) web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) @pytest.fixture(scope="function") @@ -153,7 +153,7 @@ async def test_normal_1(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) token_address_2 = await deploy_share_token_contract( @@ -165,7 +165,7 @@ async def test_normal_1(self, mock_func, processor, db): _token_2.issuer_address = issuer_address _token_2.token_address = token_address_2 _token_2.abi = {} - _token_2.version = TokenVersion.V_22_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) account = Account() @@ -300,7 +300,7 @@ async def test_normal_2(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) latest_block_number = web3.eth.block_number @@ -418,7 +418,7 @@ async def test_normal_3(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) account = Account() @@ -515,7 +515,7 @@ async def test_normal_4(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) account = Account() @@ -606,7 +606,7 @@ async def test_normal_5( _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) PersonalInfoContractTestUtils.register( @@ -791,7 +791,7 @@ async def test_normal_6(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) token_address_2 = await deploy_share_token_contract( @@ -803,7 +803,7 @@ async def test_normal_6(self, mock_func, processor, db): _token_2.issuer_address = issuer_address _token_2.token_address = token_address_2 _token_2.abi = {} - _token_2.version = TokenVersion.V_22_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) account = Account() @@ -879,7 +879,7 @@ async def test_normal_7(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) token_address_2 = await deploy_share_token_contract( @@ -891,7 +891,7 @@ async def test_normal_7(self, mock_func, processor, db): _token_2.issuer_address = issuer_address _token_2.token_address = token_address_2 _token_2.abi = {} - _token_2.version = TokenVersion.V_22_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) account = Account() @@ -1038,7 +1038,7 @@ async def test_normal_8_1(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) token_address_2 = await deploy_share_token_contract( @@ -1050,7 +1050,7 @@ async def test_normal_8_1(self, mock_func, processor, db): _token_2.issuer_address = issuer_address _token_2.token_address = token_address_2 _token_2.abi = {} - _token_2.version = TokenVersion.V_22_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) account = Account() @@ -1231,7 +1231,7 @@ async def test_normal_8_2(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) token_address_2 = await deploy_share_token_contract( @@ -1243,7 +1243,7 @@ async def test_normal_8_2(self, mock_func, processor, db): _token_2.issuer_address = issuer_address _token_2.token_address = token_address_2 _token_2.abi = {} - _token_2.version = TokenVersion.V_22_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) account = Account() @@ -1409,7 +1409,7 @@ async def test_normal_9(self, mock_func, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) account = Account() @@ -1545,7 +1545,7 @@ async def test_error_1(self, processor, db): _token_1.issuer_address = issuer_address _token_1.token_address = token_address_1 _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) db.commit() diff --git a/tests/test_batch_processor_generate_rsa_key.py b/tests/batch/test_processor_generate_rsa_key.py similarity index 100% rename from tests/test_batch_processor_generate_rsa_key.py rename to tests/batch/test_processor_generate_rsa_key.py diff --git a/tests/test_batch_processor_modify_personal_info.py b/tests/batch/test_processor_modify_personal_info.py similarity index 98% rename from tests/test_batch_processor_modify_personal_info.py rename to tests/batch/test_processor_modify_personal_info.py index 779bf13a..5580cf01 100644 --- a/tests/test_batch_processor_modify_personal_info.py +++ b/tests/batch/test_processor_modify_personal_info.py @@ -21,7 +21,7 @@ from eth_keyfile import decode_keyfile_json from sqlalchemy import func, select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.model.blockchain import ( IbetShareContract, @@ -50,7 +50,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) @pytest.fixture(scope="function") @@ -214,7 +214,7 @@ async def test_normal_1(self, processor, db): token_1.issuer_address = issuer_address_1 token_1.token_address = token_contract_address_1 token_1.abi = "abi" - token_1.version = TokenVersion.V_23_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) personal_info_contract_address_2 = deploy_personal_info_contract(user_1) @@ -227,7 +227,7 @@ async def test_normal_1(self, processor, db): token_2.issuer_address = issuer_address_1 token_2.token_address = token_contract_address_2 token_2.abi = "abi" - token_2.version = TokenVersion.V_22_12 + token_2.version = TokenVersion.V_24_06 db.add(token_2) token_contract_address_3 = await deploy_bond_token_contract(user_1, None) @@ -237,7 +237,7 @@ async def test_normal_1(self, processor, db): token_3.issuer_address = issuer_address_1 token_3.token_address = token_contract_address_3 token_3.abi = "abi" - token_3.version = TokenVersion.V_23_12 + token_3.version = TokenVersion.V_24_06 db.add(token_3) token_contract_address_4 = await deploy_share_token_contract(user_1, None) @@ -247,7 +247,7 @@ async def test_normal_1(self, processor, db): token_4.issuer_address = issuer_address_1 token_4.token_address = token_contract_address_4 token_4.abi = "abi" - token_4.version = TokenVersion.V_22_12 + token_4.version = TokenVersion.V_24_06 db.add(token_4) # PersonalInfo diff --git a/tests/test_batch_processor_monitor_block_sync.py b/tests/batch/test_processor_monitor_block_sync.py similarity index 98% rename from tests/test_batch_processor_monitor_block_sync.py rename to tests/batch/test_processor_monitor_block_sync.py index ba89d8bd..e2830226 100644 --- a/tests/test_batch_processor_monitor_block_sync.py +++ b/tests/batch/test_processor_monitor_block_sync.py @@ -23,14 +23,14 @@ import pytest from sqlalchemy import select from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.model.db import Node from batch.processor_monitor_block_sync import Processor from config import WEB3_HTTP_PROVIDER web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) @pytest.fixture(scope="function") diff --git a/tests/test_batch_processor_register_personal_info.py b/tests/batch/test_processor_register_personal_info.py similarity index 99% rename from tests/test_batch_processor_register_personal_info.py rename to tests/batch/test_processor_register_personal_info.py index bce9501b..b76baff2 100644 --- a/tests/test_batch_processor_register_personal_info.py +++ b/tests/batch/test_processor_register_personal_info.py @@ -170,7 +170,7 @@ async def test_normal_1( token_1.issuer_address = _account["address"] token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) db.commit() @@ -215,7 +215,7 @@ async def test_normal_2( token_1.issuer_address = _account["address"] token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BatchRegisterPersonalInfoUpload @@ -318,7 +318,7 @@ async def test_normal_3( token_1.issuer_address = _account["address"] token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BatchRegisterPersonalInfoUpload @@ -477,7 +477,7 @@ async def test_normal_4( token_1.issuer_address = _account["address"] token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BatchRegisterPersonalInfoUpload @@ -617,7 +617,7 @@ async def test_error_1( token_1.issuer_address = _account["address"] token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BatchRegisterPersonalInfoUpload @@ -732,7 +732,7 @@ async def test_error_2( token_1.issuer_address = _account["address"] token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BatchRegisterPersonalInfoUpload @@ -865,7 +865,7 @@ async def test_error_3( token_1.issuer_address = _account["address"] token_1.abi = token_contract_1.abi token_1.tx_hash = "tx_hash" - token_1.version = TokenVersion.V_22_12 + token_1.version = TokenVersion.V_24_06 db.add(token_1) # Prepare data : BatchRegisterPersonalInfoUpload diff --git a/tests/test_batch_processor_rotate_e2e_messaging_rsa_key.py b/tests/batch/test_processor_rotate_e2e_messaging_rsa_key.py similarity index 97% rename from tests/test_batch_processor_rotate_e2e_messaging_rsa_key.py rename to tests/batch/test_processor_rotate_e2e_messaging_rsa_key.py index 5341d357..23e9b112 100644 --- a/tests/test_batch_processor_rotate_e2e_messaging_rsa_key.py +++ b/tests/batch/test_processor_rotate_e2e_messaging_rsa_key.py @@ -19,7 +19,7 @@ import logging import time -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from unittest import mock from unittest.mock import ANY, call @@ -104,7 +104,7 @@ async def test_normal_1_2(self, processor, db): _rsa_key_1.rsa_private_key = "rsa_private_key_1" _rsa_key_1.rsa_public_key = "rsa_public_key_1" _rsa_key_1.rsa_passphrase = "rsa_passphrase_1" - _rsa_key_1.block_timestamp = datetime.utcnow() + _rsa_key_1.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key_1) db.commit() @@ -149,7 +149,7 @@ async def test_normal_1_3(self, processor, db): _rsa_key_1.rsa_private_key = "rsa_private_key_1" _rsa_key_1.rsa_public_key = "rsa_public_key_1" _rsa_key_1.rsa_passphrase = "rsa_passphrase_1" - _rsa_key_1.block_timestamp = datetime.utcnow() + _rsa_key_1.block_timestamp = datetime.now(UTC).replace(tzinfo=None) db.add(_rsa_key_1) db.commit() @@ -199,7 +199,7 @@ async def test_normal_2(self, processor, db, e2e_messaging_contract): _account.rsa_generation = 2 db.add(_account) - datetime_now = datetime.utcnow() + datetime_now = datetime.now(UTC).replace(tzinfo=None) # Prepare data : E2EMessagingAccountRsaKey _rsa_key_1_1 = E2EMessagingAccountRsaKey() @@ -270,13 +270,13 @@ async def test_normal_2(self, processor, db, e2e_messaging_contract): { "number": 12345, "timestamp": datetime( - 2099, 4, 27, 12, 34, 56, tzinfo=timezone.utc + 2099, 4, 27, 12, 34, 56, tzinfo=UTC ).timestamp(), }, { "number": 12350, "timestamp": datetime( - 2099, 4, 27, 12, 34, 59, tzinfo=timezone.utc + 2099, 4, 27, 12, 34, 59, tzinfo=UTC ).timestamp(), }, ], @@ -380,7 +380,7 @@ async def test_error_1(self, processor, db): _account.rsa_generation = 2 db.add(_account) - datetime_now = datetime.utcnow() + datetime_now = datetime.now(UTC).replace(tzinfo=None) # Prepare data : E2EMessagingAccountRsaKey _rsa_key = E2EMessagingAccountRsaKey() @@ -425,7 +425,7 @@ async def test_error_2(self, processor, db, e2e_messaging_contract): _account.rsa_generation = 2 db.add(_account) - datetime_now = datetime.utcnow() + datetime_now = datetime.now(UTC).replace(tzinfo=None) # Prepare data : E2EMessagingAccountRsaKey _rsa_key = E2EMessagingAccountRsaKey() @@ -491,7 +491,7 @@ async def test_error_3( _account.rsa_generation = 2 db.add(_account) - datetime_now = datetime.utcnow() + datetime_now = datetime.now(UTC).replace(tzinfo=None) # Prepare data : E2EMessagingAccountRsaKey _rsa_key = E2EMessagingAccountRsaKey() diff --git a/tests/test_batch_processor_scheduled_events.py b/tests/batch/test_processor_scheduled_events.py similarity index 96% rename from tests/test_batch_processor_scheduled_events.py rename to tests/batch/test_processor_scheduled_events.py index 25725ba9..b6c727b6 100644 --- a/tests/test_batch_processor_scheduled_events.py +++ b/tests/batch/test_processor_scheduled_events.py @@ -18,7 +18,7 @@ """ import logging -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from unittest.mock import patch import pytest @@ -76,11 +76,11 @@ async def test_normal_1(self, processor, db): db.add(account) # prepare data : ScheduledEvents - datetime_past_utc = datetime.now(timezone.utc) + timedelta(days=-1) + datetime_past_utc = datetime.now(UTC) + timedelta(days=-1) datetime_past_str = datetime_past_utc.isoformat() - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() - datetime_pending_utc = datetime.now(timezone.utc) + timedelta(days=1) + datetime_pending_utc = datetime.now(UTC) + timedelta(days=1) datetime_pending_str = datetime_pending_utc.isoformat() update_data = { "face_value": 10000, @@ -98,6 +98,7 @@ async def test_normal_1(self, processor, db): "is_redeemed": True, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "contact_information": "問い合わせ先test", "privacy_policy": "プライバシーポリシーtest", "is_canceled": False, @@ -186,6 +187,7 @@ async def test_normal_1(self, processor, db): "is_offering": False, "is_redeemed": True, "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "privacy_policy": "プライバシーポリシーtest", "redemption_value": 11000, "status": False, @@ -217,11 +219,11 @@ async def test_normal_2(self, processor, db): db.add(account) # prepare data : ScheduledEvents - datetime_past_utc = datetime.now(timezone.utc) + timedelta(days=-1) + datetime_past_utc = datetime.now(UTC) + timedelta(days=-1) datetime_past_str = datetime_past_utc.isoformat() - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() - datetime_pending_utc = datetime.now(timezone.utc) + timedelta(days=1) + datetime_pending_utc = datetime.now(UTC) + timedelta(days=1) datetime_pending_str = datetime_pending_utc.isoformat() update_data = { @@ -231,6 +233,7 @@ async def test_normal_2(self, processor, db): "dividend_payment_date": "20211231", "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "image_url": [ "http://sampleurl.com/some_image1.png", "http://sampleurl.com/some_image2.png", @@ -328,6 +331,7 @@ async def test_normal_2(self, processor, db): "is_canceled": False, "is_offering": False, "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "require_personal_info_registered": False, "privacy_policy": "プライバシーポリシーtest", "status": False, "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", @@ -352,7 +356,7 @@ async def test_error_1(self, processor, db): _token_address = "token_address_test" # prepare data : ScheduledEvents - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -408,7 +412,7 @@ async def test_error_2(self, processor, db): db.add(account) # prepare data : ScheduledEvents - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -466,7 +470,7 @@ async def test_error_3(self, processor, db): db.add(account) # prepare data : ScheduledEvents - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -535,7 +539,7 @@ async def test_error_4(self, processor, db): db.add(account) # prepare data : ScheduledEvents - datetime_now_jtc = datetime.now(timezone.utc) + datetime_now_jtc = datetime.now(UTC) datetime_now_str = datetime_now_jtc.isoformat() update_data = {} @@ -606,7 +610,7 @@ async def test_error_5(self, processor, db, caplog): db.add(account) # prepare data : ScheduledEvents - datetime_now_utc = datetime.now(timezone.utc) + datetime_now_utc = datetime.now(UTC) datetime_now_str = datetime_now_utc.isoformat() update_data = {} @@ -686,7 +690,7 @@ async def test_error_6(self, processor, db, caplog): db.add(account) # prepare data : ScheduledEvents - datetime_now_jtc = datetime.now(timezone.utc) + datetime_now_jtc = datetime.now(UTC) datetime_now_str = datetime_now_jtc.isoformat() update_data = {} diff --git a/tests/test_batch_processor_update_token.py b/tests/batch/test_processor_update_token.py similarity index 98% rename from tests/test_batch_processor_update_token.py rename to tests/batch/test_processor_update_token.py index a8519a2e..0287066c 100644 --- a/tests/test_batch_processor_update_token.py +++ b/tests/batch/test_processor_update_token.py @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import ANY, call, patch import pytest @@ -84,7 +84,7 @@ async def test_normal_1(self, processor, db): _token_1.token_address = _token_address_1 _token_1.abi = "" _token_1.token_status = 0 - _token_1.version = TokenVersion.V_22_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) _update_token_1 = UpdateToken() @@ -102,6 +102,7 @@ async def test_normal_1(self, processor, db): "cancellation_date": "20221231", "tradable_exchange_contract_address": "0x0000000000000000000000000000000000000001", # update "personal_info_contract_address": "0x0000000000000000000000000000000000000002", # update + "require_personal_info_registered": False, # update "transferable": False, # update "status": False, # update "is_offering": True, # update @@ -124,7 +125,7 @@ async def test_normal_1(self, processor, db): _token_2.token_address = _token_address_2 _token_2.abi = "" _token_2.token_status = 0 - _token_2.version = TokenVersion.V_23_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) _update_token_2 = UpdateToken() @@ -149,6 +150,7 @@ async def test_normal_1(self, processor, db): "is_redeemed": True, # update "tradable_exchange_contract_address": "0x0000000000000000000000000000000000000001", # update "personal_info_contract_address": "0x0000000000000000000000000000000000000002", # update + "require_personal_info_registered": False, # update "contact_information": "contact info test", # update "privacy_policy": "privacy policy test", # update "transfer_approval_required": True, # update @@ -181,9 +183,7 @@ async def test_normal_1(self, processor, db): mock_block = { "number": 12345, - "timestamp": datetime( - 2021, 4, 27, 12, 34, 56, tzinfo=timezone.utc - ).timestamp(), + "timestamp": datetime(2021, 4, 27, 12, 34, 56, tzinfo=UTC).timestamp(), } with patch( target="app.model.blockchain.token.IbetShareContract.update", @@ -210,6 +210,7 @@ async def test_normal_1(self, processor, db): dividends=None, tradable_exchange_contract_address="0x0000000000000000000000000000000000000001", personal_info_contract_address="0x0000000000000000000000000000000000000002", + require_personal_info_registered=False, transferable=False, status=False, is_offering=True, @@ -232,6 +233,7 @@ async def test_normal_1(self, processor, db): is_redeemed=True, tradable_exchange_contract_address="0x0000000000000000000000000000000000000001", personal_info_contract_address="0x0000000000000000000000000000000000000002", + require_personal_info_registered=False, contact_information="contact info test", privacy_policy="privacy policy test", transfer_approval_required=True, @@ -363,7 +365,7 @@ async def test_error_1(self, processor, db): _token_1.token_address = _token_address_1 _token_1.abi = "" _token_1.token_status = 0 - _token_1.version = TokenVersion.V_22_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) _update_token_1 = UpdateToken() @@ -401,7 +403,7 @@ async def test_error_1(self, processor, db): _token_2.token_address = _token_address_2 _token_2.abi = "" _token_2.token_status = 0 - _token_2.version = TokenVersion.V_23_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) _update_token_2 = UpdateToken() @@ -580,7 +582,7 @@ async def test_error_2(self, processor, db): _token_1.token_address = _token_address_1 _token_1.abi = "" _token_1.token_status = 0 - _token_1.version = TokenVersion.V_22_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) _update_token_1 = UpdateToken() @@ -618,7 +620,7 @@ async def test_error_2(self, processor, db): _token_2.token_address = _token_address_2 _token_2.abi = "" _token_2.token_status = 0 - _token_2.version = TokenVersion.V_23_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) _update_token_2 = UpdateToken() @@ -797,7 +799,7 @@ async def test_error_3(self, processor, db): _token_1.token_address = _token_address_1 _token_1.abi = "" _token_1.token_status = 0 - _token_1.version = TokenVersion.V_22_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) _update_token_1 = UpdateToken() @@ -835,7 +837,7 @@ async def test_error_3(self, processor, db): _token_2.token_address = _token_address_2 _token_2.abi = "" _token_2.token_status = 0 - _token_2.version = TokenVersion.V_23_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) _update_token_2 = UpdateToken() @@ -1021,7 +1023,7 @@ async def test_error_4(self, processor, db): _token_1.token_address = _token_address_1 _token_1.abi = "" _token_1.token_status = 0 - _token_1.version = TokenVersion.V_22_12 + _token_1.version = TokenVersion.V_24_06 db.add(_token_1) _update_token_1 = UpdateToken() @@ -1059,7 +1061,7 @@ async def test_error_4(self, processor, db): _token_2.token_address = _token_address_2 _token_2.abi = "" _token_2.token_status = 0 - _token_2.version = TokenVersion.V_23_12 + _token_2.version = TokenVersion.V_24_06 db.add(_token_2) _update_token_2 = UpdateToken() diff --git a/tests/conftest.py b/tests/conftest.py index 1aa577b4..c1fc02d4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,7 +24,7 @@ from httpx import AsyncClient from sqlalchemy import text from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from web3.types import RPCEndpoint from app.database import ( @@ -40,7 +40,7 @@ from tests.account_config import config_eth_account web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) @pytest_asyncio.fixture(scope="session") @@ -298,6 +298,44 @@ def ibet_security_token_escrow_contract(): return ContractUtils.get_contract("IbetSecurityTokenEscrow", contract_address) +@pytest.fixture(scope="function") +def ibet_security_token_dvp_contract(): + user_1 = config_eth_account("user1") + deployer_address = user_1["address"] + deployer_private_key = decode_keyfile_json( + raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") + ) + + # Deploy storage contract + storage_contract_address, _, _ = ContractUtils.deploy_contract( + "DVPStorage", [], deployer_address, deployer_private_key + ) + + # Deploy security token DVP contract + contract_address, _, _ = ContractUtils.deploy_contract( + "IbetSecurityTokenDVP", + [storage_contract_address], + deployer_address, + deployer_private_key, + ) + + # Upgrade version + storage_contract = ContractUtils.get_contract( + "DVPStorage", storage_contract_address + ) + tx = storage_contract.functions.upgradeVersion(contract_address).build_transaction( + { + "chainId": CHAIN_ID, + "from": deployer_address, + "gas": TX_GAS_LIMIT, + "gasPrice": 0, + } + ) + ContractUtils.send_transaction(tx, deployer_private_key) + + return ContractUtils.get_contract("IbetSecurityTokenDVP", contract_address) + + @pytest.fixture(scope="function") def e2e_messaging_contract(): user_1 = config_eth_account("user1") diff --git a/tests/utils/contract_utils.py b/tests/contract_utils.py similarity index 70% rename from tests/utils/contract_utils.py rename to tests/contract_utils.py index 0401c0a2..3c1171f5 100644 --- a/tests/utils/contract_utils.py +++ b/tests/contract_utils.py @@ -20,13 +20,13 @@ from typing import Dict from web3 import Web3 -from web3.middleware import geth_poa_middleware +from web3.middleware import ExtraDataToPOAMiddleware from app.utils.contract_utils import ContractUtils from config import CHAIN_ID, TX_GAS_LIMIT, WEB3_HTTP_PROVIDER web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) +web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) """ Helper Methods for Contract Testing @@ -385,3 +385,155 @@ def get_latest_escrow_id(contract_address: str): contract_name="IbetSecurityTokenEscrow", contract_address=contract_address ) return escrow_contract.functions.latestEscrowId().call() + + +class IbetSecurityTokenDVPContractTestUtils: + @staticmethod + def balance_of(contract_address: str, account_address: str, token_address: str): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + return dvp_contract.functions.balanceOf(account_address, token_address).call() + + @staticmethod + def create_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.createDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def cancel_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.cancelDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def confirm_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.confirmDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def finish_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.finishDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def abort_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.abortDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def get_latest_delivery_id(contract_address: str): + escrow_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + return escrow_contract.functions.latestDeliveryId().call() + + +class IbetSecurityTokenDVPContractTestUtils: + @staticmethod + def balance_of(contract_address: str, account_address: str, token_address: str): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + return dvp_contract.functions.balanceOf(account_address, token_address).call() + + @staticmethod + def create_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.createDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def cancel_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.cancelDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def confirm_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.confirmDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def finish_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.finishDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def abort_delivery( + contract_address: str, tx_from: str, private_key: str, args: list + ): + dvp_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + tx = dvp_contract.functions.abortDelivery(*args).build_transaction( + {"chainId": CHAIN_ID, "from": tx_from, "gas": TX_GAS_LIMIT, "gasPrice": 0} + ) + ContractUtils.send_transaction(transaction=tx, private_key=private_key) + + @staticmethod + def get_latest_delivery_id(contract_address: str): + escrow_contract = ContractUtils.get_contract( + contract_name="IbetSecurityTokenDVP", contract_address=contract_address + ) + return escrow_contract.functions.latestDeliveryId().call() diff --git a/tests/test_app_routers_bond_transfers_{token_address}_GET.py b/tests/test_app_routers_bond_transfers_{token_address}_GET.py deleted file mode 100644 index 0c16d570..00000000 --- a/tests/test_app_routers_bond_transfers_{token_address}_GET.py +++ /dev/null @@ -1,1267 +0,0 @@ -""" -Copyright BOOSTRY Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. - -You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - -See the License for the specific language governing permissions and -limitations under the License. - -SPDX-License-Identifier: Apache-2.0 -""" - -from datetime import datetime - -from pytz import timezone - -import config -from app.model.db import ( - IDXPersonalInfo, - IDXTransfer, - IDXTransferSourceEventType, - Token, - TokenType, - TokenVersion, -) - -local_tz = timezone(config.TZ) - - -class TestAppRoutersBondTransfersGET: - # target API endpoint - base_url = "/bond/transfers/{}" - - test_transaction_hash = "test_transaction_hash" - test_issuer_address = "test_issuer_address" - test_token_address = "test_token_address" - test_from_address = "test_from_address" - test_to_address = "test_to_address" - test_block_timestamp = [ - datetime.strptime("2022/01/02 15:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/03 - datetime.strptime("2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/02 - datetime.strptime("2022/01/02 00:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/02 - ] - test_block_timestamp_str = [ - "2022-01-03T00:20:30+09:00", - "2022-01-02T00:20:30+09:00", - "2022-01-02T09:20:30+09:00", - ] - - ########################################################################### - # Normal Case - ########################################################################### - - # - # default sort - def test_normal_1(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_23_12 - db.add(_token) - - # prepare data: IDXTransfer - for i in range(0, 3): - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = i - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[i] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get(self.base_url.format(self.test_token_address)) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": None, - "amount": 0, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": None, - "amount": 2, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": None, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - ], - } - assert resp.json() == assumed_response - - # - # offset, limit - def test_normal_2_1(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_23_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - for i in range(0, 3): - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = i - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[i] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address) + "?offset=1&limit=1" - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": 1, "limit": 1, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[2], - }, - ], - } - assert resp.json() == assumed_response - - # - # filter: source_event - def test_normal_2_2(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_23_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 0 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_1" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={"source_event": IDXTransferSourceEventType.UNLOCK.value}, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_1", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - } - ], - } - assert resp.json() == assumed_response - - # - # filter: data - def test_normal_2_3(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_23_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 0 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_1" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), params={"data": "unlo"} - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_1", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - } - ], - } - assert resp.json() == assumed_response - - # - # sort: block_timestamp ASC - def test_normal_3(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_23_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - for i in range(0, 3): - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = i - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[i] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={ - "sort_item": "block_timestamp", - "sort_order": 0, - }, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 0, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - ], - } - assert resp.json() == assumed_response - - # - # sort: from_address ASC - def test_normal_4(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_23_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 0 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_1" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={ - "sort_item": "from_address", - "sort_order": 0, - }, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_1", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_2", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 0, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_2", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - ], - } - assert resp.json() == assumed_response - - # - # sort: to_address DESC - def test_normal_5(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_23_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = "test_to_address_2" - _idx_transfer.amount = 0 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = "test_to_address_1" - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = "test_to_address_1" - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={ - "sort_item": "to_address", - "sort_order": 1, - }, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": "test_to_address_2", - "to_address_personal_information": None, - "amount": 0, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": "test_to_address_1", - "to_address_personal_information": None, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": "test_to_address_1", - "to_address_personal_information": None, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - ], - } - assert resp.json() == assumed_response - - # - # sort: amount DESC - def test_normal_6(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_23_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={ - "sort_item": "amount", - "sort_order": 0, - }, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - ], - } - assert resp.json() == assumed_response - - ########################################################################### - # Error Case - ########################################################################### - - # - # token not found - def test_error_1(self, client, db): - # request target API - resp = client.get(self.base_url.format(self.test_token_address)) - - # assertion - assert resp.status_code == 404 - assumed_response = { - "meta": {"code": 1, "title": "NotFound"}, - "detail": "token not found", - } - assert resp.json() == assumed_response - - # - # processing token - def test_error_2(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_STRAIGHT_BOND.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.token_status = 0 - _token.version = TokenVersion.V_23_12 - db.add(_token) - - db.commit() - - # request target API - resp = client.get(self.base_url.format(self.test_token_address)) - - # assertion - assert resp.status_code == 400 - assert resp.json() == { - "meta": {"code": 1, "title": "InvalidParameterError"}, - "detail": "this token is temporarily unavailable", - } - - # - # param error: sort_item - def test_error_3(self, client, db): - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={"sort_item": "block_timestamp12345"}, - ) - - # assertion - assert resp.status_code == 422 - assumed_response = { - "meta": {"code": 1, "title": "RequestValidationError"}, - "detail": [ - { - "ctx": { - "expected": "'block_timestamp', 'from_address', " - "'to_address' or 'amount'" - }, - "input": "block_timestamp12345", - "loc": ["query", "sort_item"], - "msg": "Input should be 'block_timestamp', 'from_address', " - "'to_address' or 'amount'", - "type": "enum", - } - ], - } - assert resp.json() == assumed_response - - # - # param error: sort_order(min) - def test_error_4(self, client, db): - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), params={"sort_order": -1} - ) - - # assertion - assert resp.status_code == 422 - assumed_response = { - "meta": {"code": 1, "title": "RequestValidationError"}, - "detail": [ - { - "ctx": {"expected": "0 or 1"}, - "input": -1, - "loc": ["query", "sort_order"], - "msg": "Input should be 0 or 1", - "type": "enum", - } - ], - } - assert resp.json() == assumed_response - - # - # param error: sort_order(max) - def test_error_5(self, client, db): - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), params={"sort_order": 2} - ) - - # assertion - assert resp.status_code == 422 - assumed_response = { - "meta": {"code": 1, "title": "RequestValidationError"}, - "detail": [ - { - "ctx": {"expected": "0 or 1"}, - "input": 2, - "loc": ["query", "sort_order"], - "msg": "Input should be 0 or 1", - "type": "enum", - } - ], - } - assert resp.json() == assumed_response diff --git a/tests/test_app_routers_share_transfers_{token_address}_GET.py b/tests/test_app_routers_share_transfers_{token_address}_GET.py deleted file mode 100644 index 11ebeaa9..00000000 --- a/tests/test_app_routers_share_transfers_{token_address}_GET.py +++ /dev/null @@ -1,1267 +0,0 @@ -""" -Copyright BOOSTRY Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. - -You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - -See the License for the specific language governing permissions and -limitations under the License. - -SPDX-License-Identifier: Apache-2.0 -""" - -from datetime import datetime - -from pytz import timezone - -import config -from app.model.db import ( - IDXPersonalInfo, - IDXTransfer, - IDXTransferSourceEventType, - Token, - TokenType, - TokenVersion, -) - -local_tz = timezone(config.TZ) - - -class TestAppRoutersShareTransfersGET: - # target API endpoint - base_url = "/share/transfers/{}" - - test_transaction_hash = "test_transaction_hash" - test_issuer_address = "test_issuer_address" - test_token_address = "test_token_address" - test_from_address = "test_from_address" - test_to_address = "test_to_address" - test_block_timestamp = [ - datetime.strptime("2022/01/02 15:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/03 - datetime.strptime("2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/02 - datetime.strptime("2022/01/02 00:20:30", "%Y/%m/%d %H:%M:%S"), # JST 2022/01/02 - ] - test_block_timestamp_str = [ - "2022-01-03T00:20:30+09:00", - "2022-01-02T00:20:30+09:00", - "2022-01-02T09:20:30+09:00", - ] - - ########################################################################### - # Normal Case - ########################################################################### - - # - # default sort - def test_normal_1(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_22_12 - db.add(_token) - - # prepare data: IDXTransfer - for i in range(0, 3): - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = i - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[i] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get(self.base_url.format(self.test_token_address)) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": None, - "amount": 0, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": None, - "amount": 2, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": None, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - ], - } - assert resp.json() == assumed_response - - # - # offset, limit - def test_normal_2_1(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_22_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - for i in range(0, 3): - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = i - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[i] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address) + "?offset=1&limit=1" - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": 1, "limit": 1, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[2], - }, - ], - } - assert resp.json() == assumed_response - - # - # filter: source_event - def test_normal_2_2(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_22_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 0 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_1" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={"source_event": IDXTransferSourceEventType.UNLOCK.value}, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_1", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - }, - ], - } - assert resp.json() == assumed_response - - # - # filter: data - def test_normal_2_3(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_22_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 0 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_1" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), params={"data": "unlo"} - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_1", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - } - ], - } - assert resp.json() == assumed_response - - # - # sort: block_timestamp ASC - def test_normal_3(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_22_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - for i in range(0, 3): - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = i - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[i] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={ - "sort_item": "block_timestamp", - "sort_order": 0, - }, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 0, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - ], - } - assert resp.json() == assumed_response - - # - # sort: from_address ASC - def test_normal_4(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_22_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 0 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_2" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = "test_from_address_1" - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={ - "sort_item": "from_address", - "sort_order": 0, - }, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_1", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_2", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 0, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": "test_from_address_2", - "from_address_personal_information": None, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - ], - } - assert resp.json() == assumed_response - - # - # sort: to_address DESC - def test_normal_5(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_22_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = "test_to_address_2" - _idx_transfer.amount = 0 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = "test_to_address_1" - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = "test_to_address_1" - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={ - "sort_item": "to_address", - "sort_order": 1, - }, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": "test_to_address_2", - "to_address_personal_information": None, - "amount": 0, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": "test_to_address_1", - "to_address_personal_information": None, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": "test_to_address_1", - "to_address_personal_information": None, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - ], - } - assert resp.json() == assumed_response - - # - # sort: amount DESC - def test_normal_6(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.version = TokenVersion.V_22_12 - db.add(_token) - - # prepare data: IDXPersonalInfo - _personal_info_from = IDXPersonalInfo() - _personal_info_from.account_address = self.test_from_address - _personal_info_from.issuer_address = self.test_issuer_address - _personal_info_from._personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_from) - - _personal_info_to = IDXPersonalInfo() - _personal_info_to.account_address = self.test_to_address - _personal_info_to.issuer_address = self.test_issuer_address - _personal_info_to._personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": "birth_test2", - "is_corporate": False, - "tax_category": 10, - } # latest data - db.add(_personal_info_to) - - # prepare data: IDXTransfer - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 1 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[0] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.TRANSFER.value - _idx_transfer.data = None - _idx_transfer.block_timestamp = self.test_block_timestamp[1] - db.add(_idx_transfer) - - _idx_transfer = IDXTransfer() - _idx_transfer.transaction_hash = self.test_transaction_hash - _idx_transfer.token_address = self.test_token_address - _idx_transfer.from_address = self.test_from_address - _idx_transfer.to_address = self.test_to_address - _idx_transfer.amount = 2 - _idx_transfer.source_event = IDXTransferSourceEventType.UNLOCK.value - _idx_transfer.data = {"message": "unlock"} - _idx_transfer.block_timestamp = self.test_block_timestamp[2] - db.add(_idx_transfer) - - db.commit() - - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={ - "sort_item": "amount", - "sort_order": 0, - }, - ) - - # assertion - assert resp.status_code == 200 - assumed_response = { - "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, - "transfer_history": [ - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 1, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[0], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.UNLOCK.value, - "data": {"message": "unlock"}, - "block_timestamp": self.test_block_timestamp_str[2], - }, - { - "transaction_hash": self.test_transaction_hash, - "token_address": self.test_token_address, - "from_address": self.test_from_address, - "from_address_personal_information": { - "address": "address_test1", - "birth": "birth_test1", - "email": "email_test1", - "is_corporate": False, - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "tax_category": 10, - }, - "to_address": self.test_to_address, - "to_address_personal_information": { - "address": "address_test2", - "birth": "birth_test2", - "email": "email_test2", - "is_corporate": False, - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "tax_category": 10, - }, - "amount": 2, - "source_event": IDXTransferSourceEventType.TRANSFER.value, - "data": None, - "block_timestamp": self.test_block_timestamp_str[1], - }, - ], - } - assert resp.json() == assumed_response - - ########################################################################### - # Error Case - ########################################################################### - - # - # token not found - def test_error_1(self, client, db): - # request target API - resp = client.get(self.base_url.format(self.test_token_address)) - - # assertion - assert resp.status_code == 404 - assumed_response = { - "meta": {"code": 1, "title": "NotFound"}, - "detail": "token not found", - } - assert resp.json() == assumed_response - - # - # processing token - def test_error_2(self, client, db): - # prepare data: Token - _token = Token() - _token.type = TokenType.IBET_SHARE.value - _token.tx_hash = self.test_transaction_hash - _token.issuer_address = self.test_issuer_address - _token.token_address = self.test_token_address - _token.abi = {} - _token.token_status = 0 - _token.version = TokenVersion.V_22_12 - db.add(_token) - - db.commit() - - # request target API - resp = client.get(self.base_url.format(self.test_token_address)) - - # assertion - assert resp.status_code == 400 - assert resp.json() == { - "meta": {"code": 1, "title": "InvalidParameterError"}, - "detail": "this token is temporarily unavailable", - } - - # - # param error: sort_item - def test_error_3(self, client, db): - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), - params={"sort_item": "block_timestamp12345"}, - ) - - # assertion - assert resp.status_code == 422 - assumed_response = { - "meta": {"code": 1, "title": "RequestValidationError"}, - "detail": [ - { - "ctx": { - "expected": "'block_timestamp', 'from_address', " - "'to_address' or 'amount'" - }, - "input": "block_timestamp12345", - "loc": ["query", "sort_item"], - "msg": "Input should be 'block_timestamp', 'from_address', " - "'to_address' or 'amount'", - "type": "enum", - } - ], - } - assert resp.json() == assumed_response - - # - # param error: sort_order(min) - def test_error_4(self, client, db): - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), params={"sort_order": -1} - ) - - # assertion - assert resp.status_code == 422 - assumed_response = { - "meta": {"code": 1, "title": "RequestValidationError"}, - "detail": [ - { - "ctx": {"expected": "0 or 1"}, - "input": -1, - "loc": ["query", "sort_order"], - "msg": "Input should be 0 or 1", - "type": "enum", - } - ], - } - assert resp.json() == assumed_response - - # - # param error: sort_order(max) - def test_error_5(self, client, db): - # request target API - resp = client.get( - self.base_url.format(self.test_token_address), params={"sort_order": 2} - ) - - # assertion - assert resp.status_code == 422 - assumed_response = { - "meta": {"code": 1, "title": "RequestValidationError"}, - "detail": [ - { - "ctx": {"expected": "0 or 1"}, - "input": 2, - "loc": ["query", "sort_order"], - "msg": "Input should be 0 or 1", - "type": "enum", - } - ], - } - assert resp.json() == assumed_response diff --git a/tests/test_app_routers_token_holders_personal_info_GET.py b/tests/test_app_routers_token_holders_personal_info_GET.py deleted file mode 100644 index fc15a487..00000000 --- a/tests/test_app_routers_token_holders_personal_info_GET.py +++ /dev/null @@ -1,450 +0,0 @@ -""" -Copyright BOOSTRY Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. - -You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - -See the License for the specific language governing permissions and -limitations under the License. - -SPDX-License-Identifier: Apache-2.0 -""" - -from unittest import mock - -from app.model.db import IDXPersonalInfo -from tests.account_config import config_eth_account - - -class TestAppRoutersTokenHoldersPersonalInfoGET: - # target API endpoint - url = "/token/holders/personal_info" - - ########################################################################### - # Normal Case - ########################################################################### - - # - # 0 record - def test_normal_1(self, client, db): - issuer_account = config_eth_account("user1") - issuer_address = issuer_account["address"] - - # request target API - resp = client.get( - self.url, - headers={ - "issuer-address": issuer_address, - }, - ) - - assert resp.status_code == 200 - assert resp.json() == { - "result_set": {"count": 0, "limit": None, "offset": None, "total": 0}, - "personal_info": [], - } - - # - # 1 record - def test_normal_2(self, client, db): - issuer_account = config_eth_account("user1") - issuer_address = issuer_account["address"] - account_address1 = "account_address1" - - # prepare data - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address1 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } - db.add(personal_info_idx) - - db.commit() - - # request target API - resp = client.get( - self.url, - headers={ - "issuer-address": issuer_address, - }, - ) - - assert resp.status_code == 200 - assert resp.json() == { - "result_set": {"count": 1, "limit": None, "offset": None, "total": 1}, - "personal_info": [ - { - "id": 1, - "account_address": account_address1, - "personal_info": { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - } - ], - } - - # - # Multi record - def test_normal_3(self, client, db): - issuer_account = config_eth_account("user1") - issuer_address = issuer_account["address"] - account_address1 = "account_address1" - account_address2 = "account_address2" - account_address3 = "account_address3" - - # prepare data - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address1 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } - db.add(personal_info_idx) - - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address2 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - # "birth": None, - "is_corporate": False, - "tax_category": 10, - } - db.add(personal_info_idx) - - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address3 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test3", - "name": "name_test3", - "postal_code": "postal_code_test3", - "address": "address_test3", - "email": "email_test3", - "birth": None, - "is_corporate": False, - "tax_category": 10, - } - db.add(personal_info_idx) - - db.commit() - - # request target API - resp = client.get( - self.url, - headers={ - "issuer-address": issuer_address, - }, - ) - - assert resp.status_code == 200 - assert resp.json() == { - "result_set": {"count": 3, "limit": None, "offset": None, "total": 3}, - "personal_info": [ - { - "id": 1, - "account_address": account_address1, - "personal_info": { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - }, - { - "id": 2, - "account_address": account_address2, - "personal_info": { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": None, - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - }, - { - "id": 3, - "account_address": account_address3, - "personal_info": { - "key_manager": "key_manager_test3", - "name": "name_test3", - "postal_code": "postal_code_test3", - "address": "address_test3", - "email": "email_test3", - "birth": None, - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - }, - ], - } - - # - # Pagination - def test_normal_4(self, client, db): - issuer_account = config_eth_account("user1") - issuer_address = issuer_account["address"] - account_address1 = "account_address1" - account_address2 = "account_address2" - account_address3 = "account_address3" - - # prepare data - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address1 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } - db.add(personal_info_idx) - - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address2 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - # "birth": None, - "is_corporate": False, - "tax_category": 10, - } - db.add(personal_info_idx) - - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address3 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test3", - "name": "name_test3", - "postal_code": "postal_code_test3", - "address": "address_test3", - "email": "email_test3", - "birth": None, - "is_corporate": False, - "tax_category": 10, - } - db.add(personal_info_idx) - - db.commit() - - # request target API - req_param = {"limit": 2, "offset": 1} - resp = client.get( - self.url, - params=req_param, - headers={ - "issuer-address": issuer_address, - }, - ) - - assert resp.status_code == 200 - assert resp.json() == { - "result_set": {"count": 3, "limit": 2, "offset": 1, "total": 3}, - "personal_info": [ - { - "id": 2, - "account_address": account_address2, - "personal_info": { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": None, - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - }, - { - "id": 3, - "account_address": account_address3, - "personal_info": { - "key_manager": "key_manager_test3", - "name": "name_test3", - "postal_code": "postal_code_test3", - "address": "address_test3", - "email": "email_test3", - "birth": None, - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - }, - ], - } - - # - # Sort - def test_normal_5(self, client, db): - issuer_account = config_eth_account("user1") - issuer_address = issuer_account["address"] - account_address1 = "account_address1" - account_address2 = "account_address2" - account_address3 = "account_address3" - - # prepare data - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address1 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - } - personal_info_idx.created = "2023-10-23 00:00:00" - db.add(personal_info_idx) - - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address2 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - # "birth": None, - "is_corporate": False, - "tax_category": 10, - } - personal_info_idx.created = "2023-10-23 00:00:01" - db.add(personal_info_idx) - - personal_info_idx = IDXPersonalInfo() - personal_info_idx.issuer_address = issuer_address - personal_info_idx.account_address = account_address3 - personal_info_idx.personal_info = { - "key_manager": "key_manager_test3", - "name": "name_test3", - "postal_code": "postal_code_test3", - "address": "address_test3", - "email": "email_test3", - "birth": None, - "is_corporate": False, - "tax_category": 10, - } - personal_info_idx.created = "2023-10-23 00:00:02" - db.add(personal_info_idx) - - db.commit() - - # request target API - req_param = {"sort_order": 1} - resp = client.get( - self.url, - params=req_param, - headers={ - "issuer-address": issuer_address, - }, - ) - - assert resp.status_code == 200 - assert resp.json() == { - "result_set": {"count": 3, "limit": None, "offset": None, "total": 3}, - "personal_info": [ - { - "id": 3, - "account_address": account_address3, - "personal_info": { - "key_manager": "key_manager_test3", - "name": "name_test3", - "postal_code": "postal_code_test3", - "address": "address_test3", - "email": "email_test3", - "birth": None, - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - }, - { - "id": 2, - "account_address": account_address2, - "personal_info": { - "key_manager": "key_manager_test2", - "name": "name_test2", - "postal_code": "postal_code_test2", - "address": "address_test2", - "email": "email_test2", - "birth": None, - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - }, - { - "id": 1, - "account_address": account_address1, - "personal_info": { - "key_manager": "key_manager_test1", - "name": "name_test1", - "postal_code": "postal_code_test1", - "address": "address_test1", - "email": "email_test1", - "birth": "birth_test1", - "is_corporate": False, - "tax_category": 10, - }, - "created": mock.ANY, - }, - ], - } diff --git a/tests/test_utils_ledger_utils.py b/tests/test_utils_ledger_utils.py deleted file mode 100644 index 1e45cea2..00000000 --- a/tests/test_utils_ledger_utils.py +++ /dev/null @@ -1,1178 +0,0 @@ -""" -Copyright BOOSTRY Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. - -You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - -See the License for the specific language governing permissions and -limitations under the License. - -SPDX-License-Identifier: Apache-2.0 -""" - -from datetime import datetime - -import pytest -import pytz -from eth_keyfile import decode_keyfile_json -from sqlalchemy import select -from web3 import Web3 -from web3.middleware import geth_poa_middleware - -from app.model.blockchain import ( - IbetShareContract, - IbetStraightBondContract, - PersonalInfoContract, -) -from app.model.blockchain.tx_params.ibet_share import ( - UpdateParams as IbetShareUpdateParams, -) -from app.model.blockchain.tx_params.ibet_straight_bond import ( - UpdateParams as IbetStraightBondUpdateParams, -) -from app.model.db import ( - UTXO, - Account, - AccountRsaStatus, - IDXPersonalInfo, - Ledger, - LedgerDetailsData, - LedgerDetailsDataType, - LedgerDetailsTemplate, - LedgerTemplate, - Notification, - NotificationType, - Token, - TokenType, - TokenVersion, -) -from app.utils import ledger_utils -from app.utils.contract_utils import ContractUtils -from app.utils.e2ee_utils import E2EEUtils -from config import CHAIN_ID, TX_GAS_LIMIT, TZ, WEB3_HTTP_PROVIDER, ZERO_ADDRESS -from tests.account_config import config_eth_account - -web3 = Web3(Web3.HTTPProvider(WEB3_HTTP_PROVIDER)) -web3.middleware_onion.inject(geth_poa_middleware, layer=0) - - -async def deploy_bond_token_contract( - address, private_key, personal_info_contract_address -): - arguments = [ - "token.name", - "token.symbol", - 100, - 20, - "JPY", - "token.redemption_date", - 30, - "JPY", - "token.return_date", - "token.return_amount", - "token.purpose", - ] - bond_contrat = IbetStraightBondContract() - contract_address, _, _ = await bond_contrat.create(arguments, address, private_key) - - data = IbetStraightBondUpdateParams() - data.personal_info_contract_address = personal_info_contract_address - data.face_value_currency = "JPY" - await bond_contrat.update(data, address, private_key) - - return contract_address - - -async def deploy_share_token_contract( - address, private_key, personal_info_contract_address -): - arguments = [ - "token.name", - "token.symbol", - 100, - 20, - 3, - "token.dividend_record_date", - "token.dividend_payment_date", - "token.cancellation_date", - 200, - ] - share_contract = IbetShareContract() - contract_address, _, _ = await share_contract.create( - arguments, address, private_key - ) - - data = IbetShareUpdateParams() - data.personal_info_contract_address = personal_info_contract_address - await share_contract.update(data, address, private_key) - - return contract_address - - -def deploy_personal_info_contract(address, private_key): - contract_address, _, _ = ContractUtils.deploy_contract( - "PersonalInfo", [], address, private_key - ) - return contract_address - - -async def set_personal_info_contract( - db, contract_address, issuer_account: Account, sender_list -): - contract = ContractUtils.get_contract("PersonalInfo", contract_address) - - for sender in sender_list: - tx = contract.functions.register( - issuer_account.issuer_address, "" - ).build_transaction( - { - "nonce": web3.eth.get_transaction_count(sender["address"]), - "chainId": CHAIN_ID, - "from": sender["address"], - "gas": TX_GAS_LIMIT, - "gasPrice": 0, - } - ) - ContractUtils.send_transaction(tx, sender["private_key"]) - - personal_info = PersonalInfoContract(issuer_account, contract_address) - await personal_info.modify_info(sender["address"], sender["data"]) - - -class TestCreateLedger: - ########################################################################### - # Normal Case - ########################################################################### - - # - # Share Token - @pytest.mark.asyncio - async def test_normal_1(self, db, async_db): - issuer = config_eth_account("user5") - issuer_address = issuer["address"] - issuer_private_key = decode_keyfile_json( - raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") - ) - user_1 = config_eth_account("user1") - user_address_1 = user_1["address"] - user_private_key_1 = decode_keyfile_json( - raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") - ) - user_2 = config_eth_account("user2") - user_address_2 = user_2["address"] - user_private_key_2 = decode_keyfile_json( - raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") - ) - - # prepare data - # Account - _account = Account() - _account.issuer_address = issuer_address - _account.keyfile = issuer["keyfile_json"] - _account.eoa_password = E2EEUtils.encrypt("password") - _account.rsa_private_key = issuer["rsa_private_key"] - _account.rsa_public_key = issuer["rsa_public_key"] - _account.rsa_passphrase = E2EEUtils.encrypt("password") - _account.rsa_status = AccountRsaStatus.SET.value - db.add(_account) - - # Token - personal_info_contract_address = deploy_personal_info_contract( - issuer_address, issuer_private_key - ) - await set_personal_info_contract( - db, - personal_info_contract_address, - _account, - [ - { - "address": user_address_1, - "private_key": user_private_key_1, - "data": { - "name": "name_test_con_1", - "address": "address_test_con_1", - }, - }, - { - "address": user_address_2, - "private_key": user_private_key_2, - "data": { - "name": "name_test_con_2", - "address": "address_test_con_2", - }, - }, - ], - ) - token_address_1 = await deploy_share_token_contract( - issuer_address, issuer_private_key, personal_info_contract_address - ) - _token_1 = Token() - _token_1.type = TokenType.IBET_SHARE.value - _token_1.tx_hash = "" - _token_1.issuer_address = issuer_address - _token_1.token_address = token_address_1 - _token_1.abi = {} - _token_1.version = TokenVersion.V_22_12 - db.add(_token_1) - - # IDXPersonalInfo(only user_1) - _idx_personal_info_1 = IDXPersonalInfo() - _idx_personal_info_1.account_address = user_address_1 - _idx_personal_info_1.issuer_address = issuer_address - _idx_personal_info_1.personal_info = { - "name": "name_test_db_1", - "address": "address_test_db_1", - } - db.add(_idx_personal_info_1) - - # UTXO - # user_1: "2022/01/01" = 100 + 10, "2022/01/02" = 30 + 40 - # user_2: "2022/01/01" = 200 + 20, "2022/01/02" = 40 + 2 - _utxo_1 = UTXO() - _utxo_1.transaction_hash = "tx1" - _utxo_1.account_address = user_address_1 - _utxo_1.token_address = token_address_1 - _utxo_1.amount = 100 - _utxo_1.block_number = 1 - _utxo_1.block_timestamp = datetime.strptime( - "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/01 - db.add(_utxo_1) - - _utxo_2 = UTXO() - _utxo_2.transaction_hash = "tx2" - _utxo_2.account_address = user_address_1 - _utxo_2.token_address = token_address_1 - _utxo_2.amount = 10 - _utxo_2.block_number = 2 - _utxo_2.block_timestamp = datetime.strptime( - "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/01 - db.add(_utxo_2) - - _utxo_3 = UTXO() - _utxo_3.transaction_hash = "tx3" - _utxo_3.account_address = user_address_1 - _utxo_3.token_address = token_address_1 - _utxo_3.amount = 30 - _utxo_3.block_number = 3 - _utxo_3.block_timestamp = datetime.strptime( - "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/02 - db.add(_utxo_3) - - _utxo_4 = UTXO() - _utxo_4.transaction_hash = "tx4" - _utxo_4.account_address = user_address_1 - _utxo_4.token_address = token_address_1 - _utxo_4.amount = 40 - _utxo_4.block_number = 4 - _utxo_4.block_timestamp = datetime.strptime( - "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/02 - db.add(_utxo_4) - - _utxo_5 = UTXO() - _utxo_5.transaction_hash = "tx5" - _utxo_5.account_address = user_address_2 - _utxo_5.token_address = token_address_1 - _utxo_5.amount = 200 - _utxo_5.block_number = 5 - _utxo_5.block_timestamp = datetime.strptime( - "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/01 - db.add(_utxo_5) - - _utxo_6 = UTXO() - _utxo_6.transaction_hash = "tx6" - _utxo_6.account_address = user_address_2 - _utxo_6.token_address = token_address_1 - _utxo_6.amount = 20 - _utxo_6.block_number = 6 - _utxo_6.block_timestamp = datetime.strptime( - "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/01 - db.add(_utxo_6) - - _utxo_7 = UTXO() - _utxo_7.transaction_hash = "tx7" - _utxo_7.account_address = user_address_2 - _utxo_7.token_address = token_address_1 - _utxo_7.amount = 40 - _utxo_7.block_number = 7 - _utxo_7.block_timestamp = datetime.strptime( - "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/02 - db.add(_utxo_7) - - _utxo_8 = UTXO() - _utxo_8.transaction_hash = "tx8" - _utxo_8.account_address = user_address_2 - _utxo_8.token_address = token_address_1 - _utxo_8.amount = 2 - _utxo_8.block_number = 8 - _utxo_8.block_timestamp = datetime.strptime( - "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/02 - db.add(_utxo_8) - - # Template - _template = LedgerTemplate() - _template.token_address = token_address_1 - _template.issuer_address = issuer_address - _template.headers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "テスト項目1": "テスト値1", - "テスト項目2": { - "テスト項目A": "テスト値2A", - "テスト項目B": "テスト値2B", - }, - "テスト項目3": { - "テスト項目A": {"テスト項目a": "テスト値3Aa"}, - "テスト項目B": "テスト値3B", - }, - }, - ] - _template.token_name = "受益権テスト" - _template.footers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "f-テスト項目1": "f-テスト値1", - "f-テスト項目2": { - "f-テスト項目A": "f-テスト値2A", - "f-テスト項目B": "f-テスト値2B", - }, - "f-テスト項目3": { - "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, - "f-テスト項目B": "f-テスト値3B", - }, - }, - ] - db.add(_template) - - # Template Details 1 - _details_1 = LedgerDetailsTemplate() - _details_1.token_address = token_address_1 - _details_1.token_detail_type = "優先受益権" - _details_1.headers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "test項目1": "test値1", - "test項目2": { - "test項目A": "test値2A", - }, - }, - ] - _details_1.footers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "test-item1": "test-value1", - "test-item2": {"test-itemA": {"test-itema": "test-value2Aa"}}, - }, - ] - _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value - _details_1.data_source = token_address_1 - db.add(_details_1) - - # Template Details 2 - _details_2 = LedgerDetailsTemplate() - _details_2.token_address = token_address_1 - _details_2.token_detail_type = "劣後受益権" - _details_2.headers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "d-test項目1": "d-test値1", - "d-test項目2": "d-test値2", - }, - ] - _details_2.footers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "f-d-test項目1": "d-test値1", - "f-d-test項目2": "d-test値2", - }, - ] - _details_2.data_type = LedgerDetailsDataType.DB.value - _details_2.data_source = "data_id_2" - db.add(_details_2) - - # Details 2 Data - _details_2_data_1 = LedgerDetailsData() - _details_2_data_1.token_address = token_address_1 - _details_2_data_1.data_id = "data_id_2" - _details_2_data_1.name = "test_data_name_1" - _details_2_data_1.address = "test_data_address_1" - _details_2_data_1.amount = 100 - _details_2_data_1.price = 200 - _details_2_data_1.balance = 20000 - _details_2_data_1.acquisition_date = "2022/03/03" - db.add(_details_2_data_1) - - _details_2_data_2 = LedgerDetailsData() - _details_2_data_2.token_address = token_address_1 - _details_2_data_2.data_id = "data_id_2" - _details_2_data_2.name = "test_data_name_2" - _details_2_data_2.address = "test_data_address_2" - _details_2_data_2.amount = 30 - _details_2_data_2.price = 40 - _details_2_data_2.balance = 1200 - _details_2_data_2.acquisition_date = "2022/12/03" - db.add(_details_2_data_2) - - db.commit() - - # Execute - await ledger_utils.create_ledger(token_address_1, async_db) - await async_db.commit() - await async_db.close() - - # assertion - _notifications = db.scalars(select(Notification)).all() - assert len(_notifications) == 1 - _notification = db.scalars(select(Notification).limit(1)).first() - assert _notification.id == 1 - assert _notification.notice_id is not None - assert _notification.issuer_address == issuer_address - assert _notification.priority == 0 - assert _notification.type == NotificationType.CREATE_LEDGER_INFO - assert _notification.code == 0 - assert _notification.metainfo == { - "token_address": token_address_1, - "token_type": TokenType.IBET_SHARE.value, - "ledger_id": 1, - } - - _ledger = db.scalars(select(Ledger).limit(1)).first() - assert _ledger.id == 1 - assert _ledger.token_address == token_address_1 - assert _ledger.token_type == TokenType.IBET_SHARE.value - now_ymd = datetime.now(pytz.timezone(TZ)).strftime("%Y/%m/%d") - assert _ledger.ledger == { - "created": now_ymd, - "token_name": "受益権テスト", - "currency": "", - "headers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "テスト項目1": "テスト値1", - "テスト項目2": { - "テスト項目A": "テスト値2A", - "テスト項目B": "テスト値2B", - }, - "テスト項目3": { - "テスト項目A": {"テスト項目a": "テスト値3Aa"}, - "テスト項目B": "テスト値3B", - }, - }, - ], - "details": [ - { - "token_detail_type": "優先受益権", - "headers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "test項目1": "test値1", - "test項目2": { - "test項目A": "test値2A", - }, - }, - ], - "data": [ - { - "account_address": user_address_2, - "name": "name_test_con_2", - "address": "address_test_con_2", - "amount": 220, - "price": 200, - "balance": 220 * 200, - "acquisition_date": "2022/01/01", - }, - { - "account_address": user_address_2, - "name": "name_test_con_2", - "address": "address_test_con_2", - "amount": 42, - "price": 200, - "balance": 42 * 200, - "acquisition_date": "2022/01/02", - }, - { - "account_address": user_address_1, - "name": "name_test_db_1", - "address": "address_test_db_1", - "amount": 110, - "price": 200, - "balance": 110 * 200, - "acquisition_date": "2022/01/01", - }, - { - "account_address": user_address_1, - "name": "name_test_db_1", - "address": "address_test_db_1", - "amount": 70, - "price": 200, - "balance": 70 * 200, - "acquisition_date": "2022/01/02", - }, - ], - "footers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "test-item1": "test-value1", - "test-item2": { - "test-itemA": {"test-itema": "test-value2Aa"} - }, - }, - ], - }, - { - "token_detail_type": "劣後受益権", - "headers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "d-test項目1": "d-test値1", - "d-test項目2": "d-test値2", - }, - ], - "data": [ - { - "account_address": None, - "name": "test_data_name_1", - "address": "test_data_address_1", - "amount": 100, - "price": 200, - "balance": 20000, - "acquisition_date": "2022/03/03", - }, - { - "account_address": None, - "name": "test_data_name_2", - "address": "test_data_address_2", - "amount": 30, - "price": 40, - "balance": 1200, - "acquisition_date": "2022/12/03", - }, - ], - "footers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "f-d-test項目1": "d-test値1", - "f-d-test項目2": "d-test値2", - }, - ], - }, - ], - "footers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "f-テスト項目1": "f-テスト値1", - "f-テスト項目2": { - "f-テスト項目A": "f-テスト値2A", - "f-テスト項目B": "f-テスト値2B", - }, - "f-テスト項目3": { - "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, - "f-テスト項目B": "f-テスト値3B", - }, - }, - ], - } - db.close() - - # - # Bond Token - @pytest.mark.asyncio - async def test_normal_2(self, db, async_db): - issuer = config_eth_account("user5") - issuer_address = issuer["address"] - issuer_private_key = decode_keyfile_json( - raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") - ) - user_1 = config_eth_account("user1") - user_address_1 = user_1["address"] - user_private_key_1 = decode_keyfile_json( - raw_keyfile_json=user_1["keyfile_json"], password="password".encode("utf-8") - ) - user_2 = config_eth_account("user2") - user_address_2 = user_2["address"] - user_private_key_2 = decode_keyfile_json( - raw_keyfile_json=user_2["keyfile_json"], password="password".encode("utf-8") - ) - - # prepare data - # Account - _account = Account() - _account.issuer_address = issuer_address - _account.keyfile = issuer["keyfile_json"] - _account.eoa_password = E2EEUtils.encrypt("password") - _account.rsa_private_key = issuer["rsa_private_key"] - _account.rsa_public_key = issuer["rsa_public_key"] - _account.rsa_passphrase = E2EEUtils.encrypt("password") - _account.rsa_status = AccountRsaStatus.SET.value - db.add(_account) - - # Token - personal_info_contract_address = deploy_personal_info_contract( - issuer_address, issuer_private_key - ) - await set_personal_info_contract( - db, - personal_info_contract_address, - _account, - [ - { - "address": user_address_1, - "private_key": user_private_key_1, - "data": { - "name": "name_test_con_1", - "address": "address_test_con_1", - }, - }, - { - "address": user_address_2, - "private_key": user_private_key_2, - "data": { - "name": "name_test_con_2", - "address": "address_test_con_2", - }, - }, - ], - ) - token_address_1 = await deploy_bond_token_contract( - issuer_address, issuer_private_key, personal_info_contract_address - ) - _token_1 = Token() - _token_1.type = TokenType.IBET_STRAIGHT_BOND.value - _token_1.tx_hash = "" - _token_1.issuer_address = issuer_address - _token_1.token_address = token_address_1 - _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 - db.add(_token_1) - - # IDXPersonalInfo(only user_1) - _idx_personal_info_1 = IDXPersonalInfo() - _idx_personal_info_1.account_address = user_address_1 - _idx_personal_info_1.issuer_address = issuer_address - _idx_personal_info_1.personal_info = { - "name": "name_test_db_1", - "address": "address_test_db_1", - } - db.add(_idx_personal_info_1) - - # UTXO - # user_1: "2022/01/01" = 100 + 10, "2022/01/02" = 30 + 40 - # user_2: "2022/01/01" = 200 + 20, "2022/01/02" = 40 + 2 - _utxo_1 = UTXO() - _utxo_1.transaction_hash = "tx1" - _utxo_1.account_address = user_address_1 - _utxo_1.token_address = token_address_1 - _utxo_1.amount = 100 - _utxo_1.block_number = 1 - _utxo_1.block_timestamp = datetime.strptime( - "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/01 - db.add(_utxo_1) - - _utxo_2 = UTXO() - _utxo_2.transaction_hash = "tx2" - _utxo_2.account_address = user_address_1 - _utxo_2.token_address = token_address_1 - _utxo_2.amount = 10 - _utxo_2.block_number = 2 - _utxo_2.block_timestamp = datetime.strptime( - "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/01 - db.add(_utxo_2) - - _utxo_3 = UTXO() - _utxo_3.transaction_hash = "tx3" - _utxo_3.account_address = user_address_1 - _utxo_3.token_address = token_address_1 - _utxo_3.amount = 30 - _utxo_3.block_number = 3 - _utxo_3.block_timestamp = datetime.strptime( - "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/02 - db.add(_utxo_3) - - _utxo_4 = UTXO() - _utxo_4.transaction_hash = "tx4" - _utxo_4.account_address = user_address_1 - _utxo_4.token_address = token_address_1 - _utxo_4.amount = 40 - _utxo_4.block_number = 4 - _utxo_4.block_timestamp = datetime.strptime( - "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/02 - db.add(_utxo_4) - - _utxo_5 = UTXO() - _utxo_5.transaction_hash = "tx5" - _utxo_5.account_address = user_address_2 - _utxo_5.token_address = token_address_1 - _utxo_5.amount = 200 - _utxo_5.block_number = 5 - _utxo_5.block_timestamp = datetime.strptime( - "2021/12/31 15:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/01 - db.add(_utxo_5) - - _utxo_6 = UTXO() - _utxo_6.transaction_hash = "tx6" - _utxo_6.account_address = user_address_2 - _utxo_6.token_address = token_address_1 - _utxo_6.amount = 20 - _utxo_6.block_number = 6 - _utxo_6.block_timestamp = datetime.strptime( - "2022/01/01 01:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/01 - db.add(_utxo_6) - - _utxo_7 = UTXO() - _utxo_7.transaction_hash = "tx7" - _utxo_7.account_address = user_address_2 - _utxo_7.token_address = token_address_1 - _utxo_7.amount = 40 - _utxo_7.block_number = 7 - _utxo_7.block_timestamp = datetime.strptime( - "2022/01/01 15:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/02 - db.add(_utxo_7) - - _utxo_8 = UTXO() - _utxo_8.transaction_hash = "tx8" - _utxo_8.account_address = user_address_2 - _utxo_8.token_address = token_address_1 - _utxo_8.amount = 2 - _utxo_8.block_number = 8 - _utxo_8.block_timestamp = datetime.strptime( - "2022/01/02 01:20:30", "%Y/%m/%d %H:%M:%S" - ) # JST 2022/01/02 - db.add(_utxo_8) - - # Template - - # Template - _template = LedgerTemplate() - _template.token_address = token_address_1 - _template.issuer_address = issuer_address - _template.token_name = "受益権テスト" - _template.headers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "テスト項目1": "テスト値1", - "テスト項目2": { - "テスト項目A": "テスト値2A", - "テスト項目B": "テスト値2B", - }, - "テスト項目3": { - "テスト項目A": {"テスト項目a": "テスト値3Aa"}, - "テスト項目B": "テスト値3B", - }, - }, - ] - _template.footers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "f-テスト項目1": "f-テスト値1", - "f-テスト項目2": { - "f-テスト項目A": "f-テスト値2A", - "f-テスト項目B": "f-テスト値2B", - }, - "f-テスト項目3": { - "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, - "f-テスト項目B": "f-テスト値3B", - }, - }, - ] - db.add(_template) - - # Template Details 1 - _details_1 = LedgerDetailsTemplate() - _details_1.token_address = token_address_1 - _details_1.token_detail_type = "優先受益権" - _details_1.headers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "test項目1": "test値1", - "test項目2": { - "test項目A": "test値2A", - }, - }, - ] - _details_1.footers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "test-item1": "test-value1", - "test-item2": {"test-itemA": {"test-itema": "test-value2Aa"}}, - }, - ] - _details_1.data_type = LedgerDetailsDataType.IBET_FIN.value - _details_1.data_source = token_address_1 - db.add(_details_1) - - # Template Details 2 - _details_2 = LedgerDetailsTemplate() - _details_2.token_address = token_address_1 - _details_2.token_detail_type = "劣後受益権" - _details_2.headers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "d-test項目1": "d-test値1", - "d-test項目2": "d-test値2", - }, - ] - _details_2.footers = [ - { - "key": "aaa", - "value": "bbb", - }, - { - "f-d-test項目1": "d-test値1", - "f-d-test項目2": "d-test値2", - }, - ] - _details_2.data_type = LedgerDetailsDataType.DB.value - _details_2.data_source = "data_id_2" - db.add(_details_2) - - # Details 2 Data - _details_2_data_1 = LedgerDetailsData() - _details_2_data_1.token_address = token_address_1 - _details_2_data_1.data_id = "data_id_2" - _details_2_data_1.name = "test_data_name_1" - _details_2_data_1.address = "test_data_address_1" - _details_2_data_1.amount = 100 - _details_2_data_1.price = 200 - _details_2_data_1.balance = 20000 - _details_2_data_1.acquisition_date = "2022/03/03" - db.add(_details_2_data_1) - - _details_2_data_2 = LedgerDetailsData() - _details_2_data_2.token_address = token_address_1 - _details_2_data_2.data_id = "data_id_2" - _details_2_data_2.name = "test_data_name_2" - _details_2_data_2.address = "test_data_address_2" - _details_2_data_2.amount = 30 - _details_2_data_2.price = 40 - _details_2_data_2.balance = 1200 - _details_2_data_2.acquisition_date = "2022/12/03" - db.add(_details_2_data_2) - - db.commit() - - # Execute - await ledger_utils.create_ledger(token_address_1, async_db) - await async_db.commit() - await async_db.close() - - # assertion - _notifications = db.scalars(select(Notification)).all() - assert len(_notifications) == 1 - _notification = db.scalars(select(Notification).limit(1)).first() - assert _notification.id == 1 - assert _notification.notice_id is not None - assert _notification.issuer_address == issuer_address - assert _notification.priority == 0 - assert _notification.type == NotificationType.CREATE_LEDGER_INFO - assert _notification.code == 0 - assert _notification.metainfo == { - "token_address": token_address_1, - "token_type": TokenType.IBET_STRAIGHT_BOND.value, - "ledger_id": 1, - } - _ledger = db.scalars(select(Ledger).limit(1)).first() - assert _ledger.id == 1 - assert _ledger.token_address == token_address_1 - assert _ledger.token_type == TokenType.IBET_STRAIGHT_BOND.value - now_ymd = datetime.now(pytz.timezone(TZ)).strftime("%Y/%m/%d") - assert _ledger.ledger == { - "created": now_ymd, - "token_name": "受益権テスト", - "currency": "JPY", - "headers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "テスト項目1": "テスト値1", - "テスト項目2": { - "テスト項目A": "テスト値2A", - "テスト項目B": "テスト値2B", - }, - "テスト項目3": { - "テスト項目A": {"テスト項目a": "テスト値3Aa"}, - "テスト項目B": "テスト値3B", - }, - }, - ], - "details": [ - { - "token_detail_type": "優先受益権", - "headers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "test項目1": "test値1", - "test項目2": { - "test項目A": "test値2A", - }, - }, - ], - "data": [ - { - "account_address": user_address_2, - "name": "name_test_con_2", - "address": "address_test_con_2", - "amount": 220, - "price": 20, - "balance": 220 * 20, - "acquisition_date": "2022/01/01", - }, - { - "account_address": user_address_2, - "name": "name_test_con_2", - "address": "address_test_con_2", - "amount": 42, - "price": 20, - "balance": 42 * 20, - "acquisition_date": "2022/01/02", - }, - { - "account_address": user_address_1, - "name": "name_test_db_1", - "address": "address_test_db_1", - "amount": 110, - "price": 20, - "balance": 110 * 20, - "acquisition_date": "2022/01/01", - }, - { - "account_address": user_address_1, - "name": "name_test_db_1", - "address": "address_test_db_1", - "amount": 70, - "price": 20, - "balance": 70 * 20, - "acquisition_date": "2022/01/02", - }, - ], - "footers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "test-item1": "test-value1", - "test-item2": { - "test-itemA": {"test-itema": "test-value2Aa"} - }, - }, - ], - }, - { - "token_detail_type": "劣後受益権", - "headers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "d-test項目1": "d-test値1", - "d-test項目2": "d-test値2", - }, - ], - "data": [ - { - "account_address": None, - "name": "test_data_name_1", - "address": "test_data_address_1", - "amount": 100, - "price": 200, - "balance": 20000, - "acquisition_date": "2022/03/03", - }, - { - "account_address": None, - "name": "test_data_name_2", - "address": "test_data_address_2", - "amount": 30, - "price": 40, - "balance": 1200, - "acquisition_date": "2022/12/03", - }, - ], - "footers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "f-d-test項目1": "d-test値1", - "f-d-test項目2": "d-test値2", - }, - ], - }, - ], - "footers": [ - { - "key": "aaa", - "value": "bbb", - }, - { - "f-テスト項目1": "f-テスト値1", - "f-テスト項目2": { - "f-テスト項目A": "f-テスト値2A", - "f-テスト項目B": "f-テスト値2B", - }, - "f-テスト項目3": { - "f-テスト項目A": {"f-テスト項目a": "f-テスト値3Aa"}, - "f-テスト項目B": "f-テスト値3B", - }, - }, - ], - } - db.close() - - # - # SKIP: Not Exist Template - @pytest.mark.asyncio - async def test_normal_3(self, db, async_db): - issuer = config_eth_account("user5") - issuer_address = issuer["address"] - issuer_private_key = decode_keyfile_json( - raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") - ) - - # prepare data - # Token - token_address_1 = await deploy_bond_token_contract( - issuer_address, issuer_private_key, ZERO_ADDRESS - ) - _token_1 = Token() - _token_1.type = TokenType.IBET_STRAIGHT_BOND.value - _token_1.tx_hash = "" - _token_1.issuer_address = issuer_address - _token_1.token_address = token_address_1 - _token_1.abi = {} - _token_1.version = TokenVersion.V_23_12 - db.add(_token_1) - db.commit() - - # Execute - await ledger_utils.create_ledger(token_address_1, async_db) - await async_db.commit() - await async_db.close() - - # assertion - _notifications = db.scalars(select(Notification)).all() - assert len(_notifications) == 0 - _ledger = db.scalars(select(Ledger).limit(1)).first() - assert _ledger is None - db.close() - - # - # SKIP: Other Token Type - @pytest.mark.asyncio - async def test_normal_4(self, db, async_db): - issuer = config_eth_account("user5") - issuer_address = issuer["address"] - issuer_private_key = decode_keyfile_json( - raw_keyfile_json=issuer["keyfile_json"], password="password".encode("utf-8") - ) - - # prepare data - # Token - token_address_1 = await deploy_bond_token_contract( - issuer_address, issuer_private_key, ZERO_ADDRESS - ) - _token_1 = Token() - _token_1.type = "IbetCoupon" - _token_1.tx_hash = "" - _token_1.issuer_address = issuer_address - _token_1.token_address = token_address_1 - _token_1.abi = {} - _token_1.version = TokenVersion.V_22_12 - db.add(_token_1) - - db.commit() - - # Execute - await ledger_utils.create_ledger(token_address_1, async_db) - await async_db.commit() - await async_db.close() - - # assertion - _notifications = db.scalars(select(Notification)).all() - assert len(_notifications) == 0 - _ledger = db.scalars(select(Ledger).limit(1)).first() - assert _ledger is None - db.close() - - ########################################################################### - # Error Case - ###########################################################################