diff --git a/.github/.git-pr-release b/.github/.git-pr-release new file mode 100644 index 0000000..393fe66 --- /dev/null +++ b/.github/.git-pr-release @@ -0,0 +1,4 @@ +Release <%= Time.now %> +<% pull_requests.each do |pr| -%> +- #<%= pr.number %> <%= pr.title %> <%= pr.mention %> +<% end -%> \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..336473d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @mameta diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8751582 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,21 @@ +--- +name: Bug Report +about: Create a bug report +title: '' +labels: 'bug' +assignees: '' +--- + + + +## 🐛 Description + + + +## 🔍 How to Reproduce + + + +## 📚 References + + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..ec4bb38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..6171856 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,25 @@ +--- +name: Feature Request +about: Submit a feature request +title: '' +labels: ['feature request'] +assignees: '' +--- + + + +## 🌈 Overview + + + +## ❓ Motivation + + + +## 🎨 Description + + + +## 📚 References + + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/task.md b/.github/ISSUE_TEMPLATE/task.md new file mode 100644 index 0000000..cacd9aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/task.md @@ -0,0 +1,21 @@ +--- +name: Task Template +about: Create a task +title: '' +labels: ['task'] +assignees: '' +--- + + + +## 📝 Overview + + + +## 🖋 Details + + + +## 📚 References + + \ No newline at end of file diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml new file mode 100644 index 0000000..5262982 --- /dev/null +++ b/.github/actions/install-dependencies/action.yml @@ -0,0 +1,18 @@ +name: install dependencies +description: set up python & install dependencies + +runs: + using: composite + steps: + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + - name: Install poetry + shell: bash + run: curl -sSL https://install.python-poetry.org | python3 - + - name: Install dependencies + shell: bash + run: | + poetry lock --no-update + poetry install \ No newline at end of file diff --git a/.github/actions/publish-package/action.yml b/.github/actions/publish-package/action.yml new file mode 100644 index 0000000..b591d37 --- /dev/null +++ b/.github/actions/publish-package/action.yml @@ -0,0 +1,24 @@ +name: publish package +description: publish package to PyPI +inputs: + pypi-token: + description: 'PyPI authentication token' + required: true + working-directory: + description: 'working directory' + required: true + +runs: + using: composite + steps: + - name: build & package + shell: bash + run: | + cd ${{ inputs.working-directory }} + poetry build + - name: publish package + shell: bash + run: | + cd ${{ inputs.working-directory }} + poetry config pypi-token.pypi ${{ inputs.pypi-token }} + poetry publish \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..2919e39 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ + + +## 🎨 Overview + + + +## 🌈 Details + + + +- + +## 📚 References + + \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..04bed64 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,6 @@ +template: | + ## 変更点 + + $CHANGES + + **完全な変更履歴**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION \ No newline at end of file diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..d7ccae0 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,73 @@ +name: run static checks + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +permissions: + contents: read + pull-requests: read + +jobs: + detect-changes: + runs-on: ubuntu-latest + timeout-minutes: 3 + permissions: + pull-requests: read + outputs: + root: ${{ steps.filter.outputs.root }} + sdk: ${{ steps.filter.outputs.sdk }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + root: + - '.github/**' + - 'docs/**' + - 'README.md' + - 'pyproject.toml' + - 'poetry.lock' + sdk: + - 'jpyc_sdk/**' + - 'tests/**' + - 'pyproject.toml' + + check-root: + needs: detect-changes + if: ${{ needs.detect-changes.outputs.root == 'true' }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./ + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + - name: format check + run: poetry run black --check .github docs README.md + + check-sdk: + needs: detect-changes + if: ${{ needs.detect-changes.outputs.sdk == 'true' }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./ + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + - name: type check + run: poetry run mypy jpyc_sdk + - name: lint check + run: poetry run black --check jpyc_sdk tests + - name: run tests + run: poetry run pytest \ No newline at end of file diff --git a/.github/workflows/create-release-pr.yml.disabled b/.github/workflows/create-release-pr.yml.disabled new file mode 100644 index 0000000..46b8e5b --- /dev/null +++ b/.github/workflows/create-release-pr.yml.disabled @@ -0,0 +1,27 @@ +name: create release PR + +on: + push: + branches: + - develop + +jobs: + create-release-pr: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.3 + - name: create release PR + env: + GIT_PR_RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GIT_PR_RELEASE_BRANCH_PRODUCTION: main + GIT_PR_RELEASE_BRANCH_STAGING: develop + GIT_PR_RELEASE_TEMPLATE: .github/.git-pr-release + GIT_PR_RELEASE_LABELS: release + TZ: Asia/Tokyo + run: | + gem install -N git-pr-release -v "2.2.0" + git-pr-release --no-fetch \ No newline at end of file diff --git a/.github/workflows/deploy-docs.yml.disabled b/.github/workflows/deploy-docs.yml.disabled new file mode 100644 index 0000000..cf7d2ab --- /dev/null +++ b/.github/workflows/deploy-docs.yml.disabled @@ -0,0 +1,53 @@ +name: deploy docs + +on: + push: + branches: + - main + paths: + - 'docs/**' + - 'jpyc_sdk/**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + - name: format check + run: poetry run black --check docs jpyc_sdk + + build: + needs: format + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + - name: build docs + run: | + poetry run sphinx-apidoc -f -o docs/api jpyc_sdk + poetry run sphinx-build -b html -a -E docs docs/_build/html + - uses: actions/configure-pages@v5 + - uses: actions/upload-pages-artifact@v3 + with: + path: './docs/_build/html' + + deploy: + needs: build + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/deploy-pages@v4 + id: deployment \ No newline at end of file diff --git a/.github/workflows/publish.yml.disabled b/.github/workflows/publish.yml.disabled new file mode 100644 index 0000000..7587173 --- /dev/null +++ b/.github/workflows/publish.yml.disabled @@ -0,0 +1,87 @@ +name: publish package to PyPI + +on: + push: + branches: + - main + paths: + - 'jpyc_sdk/**' + - 'pyproject.toml' + +jobs: + detect-changes: + runs-on: ubuntu-latest + timeout-minutes: 3 + permissions: + pull-requests: read + outputs: + sdk: ${{ steps.filter.outputs.sdk }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + sdk: + - 'jpyc_sdk/**' + - 'pyproject.toml' + + push-git-tag: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./ + timeout-minutes: 3 + outputs: + tag-name: ${{ 'v' }}${{ env.GIT_TAG_VERSION }} + tag-version: ${{ env.GIT_TAG_VERSION }} + tag-exists: ${{ steps.create-tag.outputs.tag_exists }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + - name: get version from `pyproject.toml` + run: echo "GIT_TAG_VERSION=$(poetry version -s)" >> $GITHUB_ENV + - uses: rickstaa/action-create-tag@v1 + id: create-tag + with: + tag: ${{ 'v' }}${{ env.GIT_TAG_VERSION }} + + publish-release-note: + needs: push-git-tag + if: ${{ needs.push-git-tag.outputs.tag-exists == 'false' }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./ + timeout-minutes: 5 + permissions: + contents: write + pull-requests: read + steps: + - uses: actions/checkout@v4 + - uses: release-drafter/release-drafter@v6 + with: + name: ${{ needs.push-git-tag.outputs.tag-name }} + tag: ${{ needs.push-git-tag.outputs.tag-name }} + version: ${{ needs.push-git-tag.outputs.tag-version }} + publish: 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-sdk: + needs: [detect-changes, publish-release-note] + if: ${{ needs.detect-changes.outputs.sdk == 'true' }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./ + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + - name: build package + run: poetry build + - uses: ./.github/actions/publish-package + with: + working-directory: ./ + pypi-token: ${{ secrets.PYPI_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f11f2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,126 @@ +# Python関連 +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ +.ruff_cache/ + +# Jupyter Notebook +.ipynb_checkpoints + +# Poetry +.venv/ +poetry.lock + +# Environment +.env +.venv +venv/ +ENV/ +.python-version + +# Documentation +docs/_build/ +docs/build/ +docs/source/api/ + +# PyCharm +.idea/ +*.iml +*.iws +*.ipr +*.iws +out/ + +# VS Code +.vscode/ +*.code-workspace +.history/ + +# macOS +.DS_Store +.AppleDouble +.LSOverride +._* + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ +*.cab +*.msi +*.msm +*.msp +*.lnk + +# Linux +*~ +.directory +.Trash-* + +# Sphinx documentation +docs/_build/ + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Coverage reports +htmlcov/ +.coverage +.coverage.* +coverage.xml +*.cover + +# Log Files +*.log \ No newline at end of file diff --git a/README.md b/README.md index 4f610d1..a9b58e8 100644 --- a/README.md +++ b/README.md @@ -1 +1,153 @@ -# python-sdk +# JPYC SDK Python + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/jcam1/python-sdks/issues/new/choose) + +Python SDK for JPYC + +## 🌈 SDK Overview + +This SDK provides Python interfaces to interact with JPYCv2 smart contracts on different chains. Currently, we support Ethereum, Polygon, Gnosis, Avalanche, Astar, and Shiden. + +## 📦 Package Structure + +Unlike the Node.js version which uses a monorepo structure with multiple packages (core and potentially others), this Python SDK has been designed as a single package for simplicity and to follow Python packaging conventions. + +This approach simplifies installation, dependency management, and usage while still providing the same functionality. If the project grows significantly in the future, we could consider splitting it into multiple packages using a tool like Poetry's namespace packages. + +## 💡 Usage + +Please follow the following steps to configure SDK. + +### 1. Installation + +Install the package using Poetry or pip. + +```bash +# Using Poetry +$ poetry add jpyc-sdk-python +``` + +```bash +# Using pip +$ pip install jpyc-sdk-python +``` + +### 2. Environment Variables + +Some data, such as configuration variables (e.g., chain name) or sensitive data (e.g., private key), are embedded as environment variables. You need to set the following environment variables. + +| Variable | Description & Instructions | +| --------------------: | :-------------------------------------------------------------------------------------------------------------------------------- | +| `SDK_ENV` | SDK environment. Set to `local` for local environment or any other sensible name for production environment. | +| `CHAIN_NAME` | Chain name. Set to one of the following\: `local`, `ethereum`, `polygon`, `gnosis`, `avalanche`, `astar` or `shiden`. | +| `NETWORK_NAME` | Network name within the specified chain. Set to one of the following\: `mainnet`, `goerli`, `sepolia`, `amoy`, `chiado` or `fuji` | +| `RPC_ENDPOINT` | RPC endpoint to send transactions. | +| `PRIVATE_KEY` | Private key of an account. | +| `LOCAL_PROXY_ADDRESS` | Proxy contract address in local environment. | + +### 3. SDK Instantiation + +Initialize an SDK instance. + +```python +from jpyc_sdk import SdkClient, JPYC +from jpyc_sdk.types import ChainName, NetworkName +import os +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() + +# 1. Initialize an SdkClient instance +sdk_client = SdkClient( + chain_name=os.getenv("CHAIN_NAME"), + network_name=os.getenv("NETWORK_NAME"), + rpc_endpoint=os.getenv("RPC_ENDPOINT") +) + +# 2. Generate an account from a private key +account = sdk_client.create_private_key_account() + +# 3. Generate a client with the account +client = sdk_client.create_local_client(account=account) + +# 4. Initialize an SDK instance +jpyc = JPYC(client=client) +``` + +### 4. SDK Usage + +Use the initialized SDK wherever you would like. + +```python +from your.path.to.initialization.file import jpyc + +# Fetch `totalSupply` from `JPYCv2` contract +total_supply = jpyc.total_supply() +print(f"totalSupply: {total_supply}") +``` + +## 🤖 Available Commands + +The following commands are available for local development & testing. + +| Command | Description | +| ---------------: | :------------------------------------------ | +| `test` | Run tests (using pytest) | +| `lint` | Run linters (mypy/black) | +| `build` | Build the SDK | +| `clean` | Clean build files | +| `docs` | Generate documentation (Markdown/HTML) | +| `docs-md` | Generate documentation in Markdown format | +| `docs-html` | Generate documentation in HTML format | + +These commands can be run as follows: + +```bash +# Run tests +$ poetry run pytest + +# Run linters +$ poetry run black jpyc_sdk tests +$ poetry run mypy jpyc_sdk + +# Generate documentation +$ poetry run docs +``` + +## 🔥 How to Contribute + +We appreciate your interest to contribute to this project! Please follow these steps to contribute: + +### 1. Create an Issue + +The first thing to do is to create a new issue. Feel free to create new issues from [here](https://github.com/jcam1/python-sdk/issues/new/choose) to propose/request new features or report bugs. + +### 2. Clone This Repository + +Next, clone this repo. Our default branch is `develop`. + +```bash +$ git clone https://github.com/yourusername/jpyc-sdk-python.git +``` + +### 3. Checkout to a New Branch + +You then need to checkout to a new branch (name whatever you would like) from the cloned `develop` branch. + +```bash +$ git checkout -b ${your_branch_name} +``` + +### 4. Write Code + +Now, write code to implement the proposed features and/or to fix bugs. + +### 5. Open a Pull Request + +Finally, open a new PR from your branch to `develop` branch, and describe what you'll have done. + +## 📚 Documentation + +You can find the auto-generated developer documentation [here](https://jcam1.github.io/python-sdk/). diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..2dd4e53 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,12 @@ +# Sphinxビルドディレクトリ +_build/ +_autosummary/ +api/ + +# 中間ファイル +_static/ +_templates/ + +# キャッシュ +__pycache__/ +.doctrees/ \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..10456c0 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..92d9ba3 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,44 @@ +API リファレンス +============= + +JPYC Python SDKの完全なAPIリファレンスです。 + +JPYC クライアント +---------------- + +.. automodule:: jpyc_sdk.client + :members: + :undoc-members: + :show-inheritance: + +JPYC コア +-------- + +.. automodule:: jpyc_sdk.jpyc + :members: + :undoc-members: + :show-inheritance: + +エンドポイント +------------ + +.. automodule:: jpyc_sdk.endpoints + :members: + :undoc-members: + :show-inheritance: + +タイプ +----- + +.. automodule:: jpyc_sdk.types + :members: + :undoc-members: + :show-inheritance: + +ユーティリティ +------------ + +.. automodule:: jpyc_sdk.utils + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/build_docs.bat b/docs/build_docs.bat new file mode 100644 index 0000000..19ad0d1 --- /dev/null +++ b/docs/build_docs.bat @@ -0,0 +1,12 @@ +@echo off +REM ドキュメント生成用のバッチファイル + +REM APIドキュメントを生成 +echo APIドキュメントを生成中... +poetry run sphinx-apidoc -f -o docs/api jpyc_sdk + +REM HTMLドキュメントをビルド +echo HTMLドキュメントをビルド中... +poetry run sphinx-build -b html -a -E docs docs/_build/html + +echo ドキュメント生成完了!docs/_build/html ディレクトリを確認してください。 \ No newline at end of file diff --git a/docs/build_docs.sh b/docs/build_docs.sh new file mode 100755 index 0000000..dd2f912 --- /dev/null +++ b/docs/build_docs.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# ドキュメント生成用のシェルスクリプト + +# APIドキュメントを生成 +echo "APIドキュメントを生成中..." +poetry run sphinx-apidoc -f -o docs/api jpyc_sdk + +# HTMLドキュメントをビルド +echo "HTMLドキュメントをビルド中..." +poetry run sphinx-build -b html -a -E docs docs/_build/html + +echo "ドキュメント生成完了!docs/_build/html ディレクトリを確認してください。" \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..0562c1e --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,45 @@ +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + +# プロジェクト情報 +project = 'JPYC Python SDK' +copyright = '2024, JPYC Inc.' +author = 'JPYC Inc.' +version = '0.1.0' + +# 拡張機能 +extensions = [ + 'sphinx.ext.autodoc', # 自動ドキュメント生成 + 'sphinx.ext.viewcode', # ソースコードへのリンク + 'sphinx.ext.napoleon', # Google/Numpyスタイルdocstringsのサポート + 'sphinx.ext.autosummary', # 自動サマリー生成 + 'sphinx.ext.githubpages', # GitHub Pagesのサポート + # 'myst_parser', # Markdownサポート - 必要ない場合はコメントアウト +] + +# テーマ設定 +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] + +# 自動APIドキュメント生成の設定 +autosummary_generate = True # autosummaryを使用して自動生成 +autodoc_typehints = 'description' +autodoc_member_order = 'bysource' +autoclass_content = 'both' + +# 出力オプション +html_show_sourcelink = True +html_show_sphinx = False + +# 言語設定 +language = 'ja' + +# ソースコードへのリンク +html_context = { + "display_github": True, + "github_user": "jcam1", + "github_repo": "python-sdk", + "github_version": "main", + "conf_py_path": "/docs/", +} \ No newline at end of file diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000..a392a30 --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1,73 @@ +貢献方法 +======== + +このプロジェクトへの貢献に興味をお持ちいただきありがとうございます!以下の手順に従ってこのプロジェクトに貢献する方法を確認してください。 + +1. イシューの作成 +-------------- + +最初にすべきことは、新しいイシューを作成することです。新機能の提案やバグの報告をするための新しいイシューを作成してください。 + +2. リポジトリのクローン +------------------- + +次に、このリポジトリをクローンします。デフォルトブランチは `main` です。 + +.. code-block:: bash + + $ git clone https://github.com/JPYC/python-sdk.git + +3. 新しいブランチへのチェックアウト +------------------------------ + +クローンした `main` ブランチから新しいブランチ(任意の名前を付けてください)にチェックアウトする必要があります。 + +.. code-block:: bash + + $ git checkout -b <あなたのブランチ名> + +4. コードの作成 +----------- + +次に、提案された機能を実装したり、バグを修正したりするためのコードを作成します。 + +5. テストの実行 +------------ + +変更を加えた後、すべてのテストが通過することを確認してください: + +.. code-block:: bash + + $ poetry run pytest + +6. コードのフォーマット +------------------- + +コードをフォーマットし、linterエラーがないことを確認します: + +.. code-block:: bash + + $ poetry run black jpyc_sdk tests + $ poetry run mypy jpyc_sdk + +7. プルリクエストのオープン +----------------------- + +最後に、あなたのブランチから `main` ブランチへの新しいPRをオープンし、あなたが行ったことを説明してください。 + +ドキュメントの更新 +-------------- + +コードを変更した場合は、関連するドキュメントも更新してください。ドキュメントは以下のコマンドで生成できます: + +.. code-block:: bash + + $ poetry run sphinx-build -b html docs docs/_build/html + +コーディング規約 +------------ + +- PEP 8に従ってください +- すべての新しい機能にはテストを書いてください +- すべての公開関数とクラスにはdocstringを追加してください +- type hintを使用してください \ No newline at end of file diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..2c212e4 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,102 @@ +コード例 +====== + +JPYC Python SDKを使用する基本的な例を以下に示します。 + +サンプルコード +=========== + +トークン残高の取得 +-------------- + +.. code-block:: python + + import asyncio + from jpyc_sdk import SdkClient, JPYC + from dotenv import load_dotenv + import os + + async def get_balance(): + # 環境変数をロード + load_dotenv() + + # SDKクライアントの初期化 + sdk_client = SdkClient( + chain_name=os.getenv("CHAIN_NAME"), + network_name=os.getenv("NETWORK_NAME"), + rpc_endpoint=os.getenv("RPC_ENDPOINT") + ) + + # アカウントとクライアントの作成 + account = sdk_client.create_private_key_account() + client = sdk_client.create_local_client(account=account) + + # JPYCインスタンスの初期化 + jpyc = JPYC(client=client) + + # アドレスの残高を取得 + address = "0x..." # 残高を確認したいアドレス + balance = await jpyc.balance_of(address) + + print(f"Balance: {balance}") + + # 非同期関数を実行 + asyncio.run(get_balance()) + +トークンの送信 +----------- + +.. code-block:: python + + import asyncio + from jpyc_sdk import SdkClient, JPYC + from dotenv import load_dotenv + import os + + async def transfer_tokens(): + # 環境変数をロード + load_dotenv() + + # SDKクライアントの初期化 + sdk_client = SdkClient( + chain_name=os.getenv("CHAIN_NAME"), + network_name=os.getenv("NETWORK_NAME"), + rpc_endpoint=os.getenv("RPC_ENDPOINT") + ) + + # アカウントとクライアントの作成 + account = sdk_client.create_private_key_account() + client = sdk_client.create_local_client(account=account) + + # JPYCインスタンスの初期化 + jpyc = JPYC(client=client) + + # トークンを送信する宛先アドレスと金額 + recipient = "0x..." # 送信先アドレス + amount = 1000 # 送信金額(単位はJPYC) + + # 送信前の残高を確認 + sender_balance_before = await jpyc.balance_of(account.address) + recipient_balance_before = await jpyc.balance_of(recipient) + + print(f"Sender balance before: {sender_balance_before}") + print(f"Recipient balance before: {recipient_balance_before}") + + # トークンを送信 + tx_hash = await jpyc.transfer(recipient, amount) + print(f"Transaction hash: {tx_hash}") + + # トランザクションの完了を待機(実際の実装ではブロック確認を待つ方が良い) + await asyncio.sleep(15) + + # 送信後の残高を確認 + sender_balance_after = await jpyc.balance_of(account.address) + recipient_balance_after = await jpyc.balance_of(recipient) + + print(f"Sender balance after: {sender_balance_after}") + print(f"Recipient balance after: {recipient_balance_after}") + + # 非同期関数を実行 + asyncio.run(transfer_tokens()) + +より多くのサンプルについては、別のリポジトリにあるサンプルコードを参照してください。 \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..e542700 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,32 @@ +JPYC Python SDK ドキュメント +======================= + +.. image:: https://img.shields.io/badge/License-MIT-yellow.svg + :alt: License: MIT + +JPYC Python SDKは、さまざまなチェーン上の `JPYCv2コントラクト `_ と対話するためのPythonインターフェースを実装しています。 + +.. toctree:: + :maxdepth: 2 + :caption: 目次: + + installation + usage + modules + examples + +API リファレンス +-------------- + +.. autosummary:: + :toctree: _autosummary + :recursive: + + jpyc_sdk + +インデックス +----------- + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..a0ff53d --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,28 @@ +インストール +======== + +Poetry を使用したインストール +------------------------- + +.. code-block:: bash + + $ poetry add jpyc-sdk-python + +Pip を使用したインストール +----------------------- + +.. code-block:: bash + + $ pip install jpyc-sdk-python + +依存関係 +------- + +JPYC Python SDKには以下の依存関係があります: + +- Python >=3.11 +- web3 >=7.9.0,<8.0.0 +- pydantic >=2.10.6 +- typing-extensions >=4.12.2 +- python-dotenv >=1.0.1 +- hexbytes >=1.2.0 \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..9d79dd3 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd \ No newline at end of file diff --git a/docs/modules.rst b/docs/modules.rst new file mode 100644 index 0000000..d31533e --- /dev/null +++ b/docs/modules.rst @@ -0,0 +1,7 @@ +API ドキュメント +============= + +.. toctree:: + :maxdepth: 4 + + api/jpyc_sdk \ No newline at end of file diff --git a/docs/usage.rst b/docs/usage.rst new file mode 100644 index 0000000..d1ea33a --- /dev/null +++ b/docs/usage.rst @@ -0,0 +1,53 @@ +使用方法 +====== + +基本的な使い方 +----------- + +.. code-block:: python + + from jpyc_sdk import SdkClient, JPYC + import os + from dotenv import load_dotenv + + # 環境変数をロード + load_dotenv() + + # SdkClientインスタンスを初期化 + sdk_client = SdkClient( + chain_name=os.getenv("CHAIN_NAME"), + network_name=os.getenv("NETWORK_NAME"), + rpc_endpoint=os.getenv("RPC_ENDPOINT") + ) + + # アカウントとクライアントの作成 + account = sdk_client.create_private_key_account() + client = sdk_client.create_local_client(account=account) + + # JPYCインスタンスの初期化 + jpyc = JPYC(client=client) + + # トークンの総供給量を取得 + async def get_total_supply(): + total_supply = await jpyc.total_supply() + print(f"Total Supply: {total_supply}") + +環境変数 +------- + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - 環境変数 + - 説明 + * - ``SDK_ENV`` + - SDKの環境(``local``など) + * - ``CHAIN_NAME`` + - チェーン名(``ethereum``, ``polygon``など) + * - ``NETWORK_NAME`` + - ネットワーク名(``mainnet``, ``sepolia``など) + * - ``RPC_ENDPOINT`` + - RPC接続先URL + * - ``PRIVATE_KEY`` + - アカウントの秘密鍵 \ No newline at end of file diff --git a/jpyc_sdk/__init__.py b/jpyc_sdk/__init__.py new file mode 100644 index 0000000..2c140b1 --- /dev/null +++ b/jpyc_sdk/__init__.py @@ -0,0 +1,32 @@ +""" +JPYC Python SDK + +Python SDK for interacting with JPYC stablecoin +""" + +import os +import dotenv +dotenv.load_dotenv() + +# Version information +__version__ = "0.1.0" + +# Client related +from .client import SdkClient, ISdkClient + +# JPYC core +from .jpyc import JPYC, IJPYC + +# Types and endpoints +from .utils.types import ChainName, NetworkName, Endpoint + +# Register all public APIs in __all__ +__all__ = [ + "SdkClient", + "ISdkClient", + "JPYC", + "IJPYC", + "ChainName", + "NetworkName", + "Endpoint", +] \ No newline at end of file diff --git a/jpyc_sdk/artifacts/JPYCv2/contracts/v1/FiatTokenV1.sol/FiatTokenV1.json b/jpyc_sdk/artifacts/JPYCv2/contracts/v1/FiatTokenV1.sol/FiatTokenV1.json new file mode 100644 index 0000000..d685095 --- /dev/null +++ b/jpyc_sdk/artifacts/JPYCv2/contracts/v1/FiatTokenV1.sol/FiatTokenV1.json @@ -0,0 +1,1260 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "FiatTokenV1", + "sourceName": "JPYCv2/contracts/v1/FiatTokenV1.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "authorizer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "nonce", + "type": "bytes32" + } + ], + "name": "AuthorizationCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "authorizer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "nonce", + "type": "bytes32" + } + ], + "name": "AuthorizationUsed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "Blocklisted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newBlocklister", + "type": "address" + } + ], + "name": "BlocklisterChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "burner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newMinterAdmin", + "type": "address" + } + ], + "name": "MinterAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "minterAllowedAmount", + "type": "uint256" + } + ], + "name": "MinterConfigured", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldMinter", + "type": "address" + } + ], + "name": "MinterRemoved", + "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": [], + "name": "Pause", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "PauserChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newRescuer", + "type": "address" + } + ], + "name": "RescuerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "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": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "UnBlocklisted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "Unpause", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "CANCEL_AUTHORIZATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "RECEIVE_WITH_AUTHORIZATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TRANSFER_WITH_AUTHORIZATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "_domainSeparatorV4", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "authorizer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "nonce", + "type": "bytes32" + } + ], + "name": "authorizationState", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "blocklist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "blocklister", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "authorizer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "nonce", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "cancelAuthorization", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minterAllowedAmount", + "type": "uint256" + } + ], + "name": "configureMinter", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currency", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "decrement", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "increment", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "tokenName", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenCurrency", + "type": "string" + }, + { + "internalType": "uint8", + "name": "tokenDecimals", + "type": "uint8" + }, + { + "internalType": "address", + "name": "newMinterAdmin", + "type": "address" + }, + { + "internalType": "address", + "name": "newPauser", + "type": "address" + }, + { + "internalType": "address", + "name": "newBlocklister", + "type": "address" + }, + { + "internalType": "address", + "name": "newRescuer", + "type": "address" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "isBlocklisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isMinter", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "minterAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "minterAllowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "nonces", + "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": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pauser", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validAfter", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validBefore", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "nonce", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "receiveWithAuthorization", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + } + ], + "name": "removeMinter", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "tokenContract", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "rescueERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "rescuer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validAfter", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validBefore", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "nonce", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "transferWithAuthorization", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "unBlocklist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newBlocklister", + "type": "address" + } + ], + "name": "updateBlocklister", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newMinterAdmin", + "type": "address" + } + ], + "name": "updateMinterAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newPauser", + "type": "address" + } + ], + "name": "updatePauser", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newRescuer", + "type": "address" + } + ], + "name": "updateRescuer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "bytecode": "0x60a0604052306080523480156200001557600080fd5b50620000213362000027565b62000077565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b608051614e33620000af600039600081816111da0152818161127001528181611f0e01528181611fa4015261209f0152614e336000f3fe6080604052600436106103295760003560e01c80637ecebe00116101a5578063aa271e1a116100ec578063e3ee160e11610095578063e94a01021161006f578063e94a0102146109d7578063ef55bec614610a1e578063f2fde38b14610a3e578063f9b5aa9214610a5e57600080fd5b8063e3ee160e14610982578063e5a6b10f146109a2578063e5c7160b146109b757600080fd5b8063d505accf116100c6578063d505accf146108e7578063d916948714610907578063dd62ed3e1461093b57600080fd5b8063aa271e1a1461086d578063b2118a8d146108a7578063b54d9497146108c757600080fd5b806395d89b411161014e578063a457c2d711610128578063a457c2d71461080c578063a754d48f1461082c578063a9059cbb1461084d57600080fd5b806395d89b41146107a35780639fd0506d146107b8578063a0cc6a68146107d857600080fd5b80638a6db9c31161017f5780638a6db9c3146107155780638da5cb5b1461074c5780638e204c431461076a57600080fd5b80637ecebe00146106955780637f2eecc3146106cc5780638456cb591461070057600080fd5b80633f4ba83a1161027457806352d1902d1161021d5780635c975abb116101f75780635c975abb1461060857806370a082311461062957806374ebf673146106605780637b134b4c1461068057600080fd5b806352d1902d146105b3578063554bab3c146105c85780635a049a70146105e857600080fd5b8063439531fd1161024e578063439531fd146105605780634e44d956146105805780634f1ef286146105a057600080fd5b80633f4ba83a1461050b57806340c10f191461052057806342966c681461054057600080fd5b806330adf81f116102d65780633659cfe6116102b05780633659cfe61461049357806338a63183146104b357806339509351146104eb57600080fd5b806330adf81f1461040b578063313ce5671461043f57806331b230201461047357600080fd5b806323b872dd1161030757806323b872dd146103a95780632ab60045146103c95780633092afd5146103eb57600080fd5b806306fdde031461032e578063095ea7b31461035957806318160ddd14610389575b600080fd5b34801561033a57600080fd5b50610343610a7e565b604051610350919061481c565b60405180910390f35b34801561036557600080fd5b5061037961037436600461486f565b610b0d565b6040519015158152602001610350565b34801561039557600080fd5b50610202545b604051908152602001610350565b3480156103b557600080fd5b506103796103c436600461489b565b610c59565b3480156103d557600080fd5b506103e96103e43660046148dc565b610eed565b005b3480156103f757600080fd5b506103796104063660046148dc565b611029565b34801561041757600080fd5b5061039b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b34801561044b57600080fd5b506102035461046190600160a01b900460ff1681565b60405160ff9091168152602001610350565b34801561047f57600080fd5b506103e961048e3660046148dc565b61110b565b34801561049f57600080fd5b506103e96104ae3660046148dc565b6111cf565b3480156104bf57600080fd5b50609a546104d3906001600160a01b031681565b6040516001600160a01b039091168152602001610350565b3480156104f757600080fd5b5061037961050636600461486f565b61136d565b34801561051757600080fd5b506103e96114a9565b34801561052c57600080fd5b5061037961053b36600461486f565b61157c565b34801561054c57600080fd5b506103e961055b3660046148f9565b6119a7565b34801561056c57600080fd5b506103e961057b3660046148dc565b611c89565b34801561058c57600080fd5b5061037961059b36600461486f565b611dc6565b6103e96105ae3660046149b7565b611f03565b3480156105bf57600080fd5b5061039b612092565b3480156105d457600080fd5b506103e96105e33660046148dc565b612157565b3480156105f457600080fd5b506103e9610603366004614a2c565b612293565b34801561061457600080fd5b5060335461037990600160a01b900460ff1681565b34801561063557600080fd5b5061039b6106443660046148dc565b6001600160a01b03166000908152610204602052604090205490565b34801561066c57600080fd5b506103e961067b366004614a9c565b6122f4565b34801561068c57600080fd5b5061039b612838565b3480156106a157600080fd5b5061039b6106b03660046148dc565b6001600160a01b03166000908152610168602052604090205490565b3480156106d857600080fd5b5061039b7fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b34801561070c57600080fd5b506103e96129de565b34801561072157600080fd5b5061039b6107303660046148dc565b6001600160a01b03166000908152610207602052604090205490565b34801561075857600080fd5b506000546001600160a01b03166104d3565b34801561077657600080fd5b506103796107853660046148dc565b6001600160a01b031660009081526067602052604090205460011490565b3480156107af57600080fd5b50610343612ab7565b3480156107c457600080fd5b506033546104d3906001600160a01b031681565b3480156107e457600080fd5b5061039b7f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b34801561081857600080fd5b5061037961082736600461486f565b612ac5565b34801561083857600080fd5b50610203546104d3906001600160a01b031681565b34801561085957600080fd5b5061037961086836600461486f565b612c01565b34801561087957600080fd5b506103796108883660046148dc565b6001600160a01b03166000908152610206602052604090205460ff1690565b3480156108b357600080fd5b506103e96108c236600461489b565b612d3d565b3480156108d357600080fd5b506066546104d3906001600160a01b031681565b3480156108f357600080fd5b506103e9610902366004614b89565b612e4e565b34801561091357600080fd5b5061039b7f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b34801561094757600080fd5b5061039b610956366004614bf7565b6001600160a01b0391821660009081526102056020908152604080832093909416825291909152205490565b34801561098e57600080fd5b506103e961099d366004614c30565b612fa1565b3480156109ae57600080fd5b506103436130f8565b3480156109c357600080fd5b506103e96109d23660046148dc565b613106565b3480156109e357600080fd5b506103796109f236600461486f565b6001600160a01b0391909116600090815261013560209081526040808320938352929052205460011490565b348015610a2a57600080fd5b506103e9610a39366004614c30565b6131cb565b348015610a4a57600080fd5b506103e9610a593660046148dc565b613315565b348015610a6a57600080fd5b506103e9610a793660046148dc565b613403565b6101ff8054610a8c90614cb2565b80601f0160208091040260200160405190810160405280929190818152602001828054610ab890614cb2565b8015610b055780601f10610ada57610100808354040283529160200191610b05565b820191906000526020600020905b815481529060010190602001808311610ae857829003601f168201915b505050505081565b603354600090600160a01b900460ff1615610b625760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b3360008181526067602052604090205415610bcd5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038416600090815260676020526040902054849015610c435760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b610c4e33868661353f565b506001949350505050565b603354600090600160a01b900460ff1615610ca95760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b3360008181526067602052604090205415610d145760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038516600090815260676020526040902054859015610d8a5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038516600090815260676020526040902054859015610e005760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0387166000908152610205602090815260408083203384529091529020546000198114610ed45785811015610ea45760405162461bcd60e51b815260206004820152602c60248201527f46696174546f6b656e3a207472616e7366657220616d6f756e7420657863656560448201527f647320616c6c6f77616e636500000000000000000000000000000000000000006064820152608401610b59565b610eae8682614d35565b6001600160a01b0389166000908152610205602090815260408083203384529091529020555b610edf888888613691565b506001979650505050505050565b33610f006000546001600160a01b031690565b6001600160a01b031614610f565760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b038116610fd25760405162461bcd60e51b815260206004820152602a60248201527f526573637561626c653a206e6577207265736375657220697320746865207a6560448201527f726f2061646472657373000000000000000000000000000000000000000000006064820152608401610b59565b609a805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040517fe475e580d85111348e40d8ca33cfdd74c30fe1655c2d8537a13abc10065ffa5a90600090a250565b610203546000906001600160a01b031633146110ad5760405162461bcd60e51b815260206004820152602860248201527f46696174546f6b656e3a2063616c6c6572206973206e6f7420746865206d696e60448201527f74657241646d696e0000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b038216600081815261020660209081526040808320805460ff19169055610207909152808220829055517fe94479a9f7e1952cc78f2d6baab678adc1b772d936c6583def489e524cb666929190a25060015b919050565b6066546001600160a01b0316331461118b5760405162461bcd60e51b815260206004820152602c60248201527f426c6f636b6c69737461626c653a2063616c6c6572206973206e6f742074686560448201527f20626c6f636b6c697374657200000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b038116600081815260676020526040808220829055517fbc3fe0fc667d12a7a22748747f024a7d971127ffc48f6622675d3e97a2591a519190a250565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016141561126e5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610b59565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166112c97f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b0316146113455760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610b59565b61134e816138b7565b6040805160008082526020820190925261136a91839190613920565b50565b603354600090600160a01b900460ff16156113bd5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b33600081815260676020526040902054156114285760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b03841660009081526067602052604090205484901561149e5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b610c4e338686613ac5565b6033546001600160a01b031633146115295760405162461bcd60e51b815260206004820152602260248201527f5061757361626c653a2063616c6c6572206973206e6f7420746865207061757360448201527f65720000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b603380547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f7805862f689e2f13df9f062ff482ad3ad112aca9e0847911ed832e158c525b3390600090a1565b603354600090600160a01b900460ff16156115cc5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b336000908152610206602052604090205460ff166116525760405162461bcd60e51b815260206004820152602160248201527f46696174546f6b656e3a2063616c6c6572206973206e6f742061206d696e746560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b33600081815260676020526040902054156116bd5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0384166000908152606760205260409020548490156117335760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0385166117af5760405162461bcd60e51b815260206004820152602360248201527f46696174546f6b656e3a206d696e7420746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610b59565b600084116118255760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a206d696e7420616d6f756e74206e6f7420677265617460448201527f6572207468616e203000000000000000000000000000000000000000000000006064820152608401610b59565b3360009081526102076020526040902054808511156118ac5760405162461bcd60e51b815260206004820152602e60248201527f46696174546f6b656e3a206d696e7420616d6f756e742065786365656473206d60448201527f696e746572416c6c6f77616e63650000000000000000000000000000000000006064820152608401610b59565b84610202546118bb9190614d4c565b610202556001600160a01b038616600090815261020460205260409020546118e4908690614d4c565b6001600160a01b038716600090815261020460205260409020556119088582614d35565b336000818152610207602090815260409182902093909355518781526001600160a01b038916927fab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8910160405180910390a36040518581526001600160a01b038716906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a350600195945050505050565b603354600160a01b900460ff16156119f45760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b336000908152610206602052604090205460ff16611a7a5760405162461bcd60e51b815260206004820152602160248201527f46696174546f6b656e3a2063616c6c6572206973206e6f742061206d696e746560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b3360008181526067602052604090205415611ae55760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b336000908152610204602052604090205482611b695760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a206275726e20616d6f756e74206e6f7420677265617460448201527f6572207468616e203000000000000000000000000000000000000000000000006064820152608401610b59565b82811015611bdf5760405162461bcd60e51b815260206004820152602660248201527f46696174546f6b656e3a206275726e20616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610b59565b8261020254611bee9190614d35565b61020255611bfc8382614d35565b3360008181526102046020526040908190209290925590517fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca590611c439086815260200190565b60405180910390a260405183815260009033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a3505050565b33611c9c6000546001600160a01b031690565b6001600160a01b031614611cf25760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b038116611d6e5760405162461bcd60e51b815260206004820152602e60248201527f46696174546f6b656e3a206e6577206d696e74657241646d696e20697320746860448201527f65207a65726f20616464726573730000000000000000000000000000000000006064820152608401610b59565b610203805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040517f4e6db312b79f0cdadbee5f76e2473786c1e81cba2356eacccc2aa5b5e6e3664c90600090a250565b603354600090600160a01b900460ff1615611e165760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b610203546001600160a01b03163314611e975760405162461bcd60e51b815260206004820152602860248201527f46696174546f6b656e3a2063616c6c6572206973206e6f7420746865206d696e60448201527f74657241646d696e0000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b038316600081815261020660209081526040808320805460ff1916600117905561020782529182902085905590518481527f46980fca912ef9bcdbd36877427b6b90e860769f604e89c0e67720cece530d20910160405180910390a250600192915050565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161415611fa25760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610b59565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611ffd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b0316146120795760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610b59565b612082826138b7565b61208e82826001613920565b5050565b6000306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146121325760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152608401610b59565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b3361216a6000546001600160a01b031690565b6001600160a01b0316146121c05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b03811661223c5760405162461bcd60e51b815260206004820152602860248201527f5061757361626c653a206e65772070617573657220697320746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401610b59565b6033805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040517fb80482a293ca2e013eda8683c9bd7fc8347cfdaeea5ede58cba46df502c2a60490600090a250565b603354600160a01b900460ff16156122e05760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b6122ed8585858585613b04565b5050505050565b610203547501000000000000000000000000000000000000000000900460ff16156123875760405162461bcd60e51b815260206004820152602a60248201527f46696174546f6b656e3a20636f6e747261637420697320616c7265616479206960448201527f6e697469616c697a6564000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0385166124035760405162461bcd60e51b815260206004820152602e60248201527f46696174546f6b656e3a206e6577206d696e74657241646d696e20697320746860448201527f65207a65726f20616464726573730000000000000000000000000000000000006064820152608401610b59565b6001600160a01b03841661247f5760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a206e65772070617573657220697320746865207a657260448201527f6f206164647265737300000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0383166124fb5760405162461bcd60e51b815260206004820152602e60248201527f46696174546f6b656e3a206e657720626c6f636b6c697374657220697320746860448201527f65207a65726f20616464726573730000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0382166125775760405162461bcd60e51b815260206004820152602a60248201527f46696174546f6b656e3a206e6577207265736375657220697320746865207a6560448201527f726f2061646472657373000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0381166125f35760405162461bcd60e51b815260206004820152602860248201527f46696174546f6b656e3a206e6577206f776e657220697320746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401610b59565b8851612607906101ff9060208c0190614757565b50875161261c906102009060208b0190614757565b508651612631906102019060208a0190614757565b5061020380547fffffffffffffffffffffff00000000000000000000000000000000000000000016600160a01b60ff89160273ffffffffffffffffffffffffffffffffffffffff19908116919091176001600160a01b0388811691909117909255603380548216878416179055606680548216868416179055609a80549091169184169190911790556126c381613c25565b30600081815260676020908152604091829020600190819055825180840184529081527f3100000000000000000000000000000000000000000000000000000000000000908201528b518c82012082517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81840152808401919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015246608082015260a0808201949094528251808203909401845260c001909152815191012060ff55466101005588516127a5906101019060208c0190614757565b506040805180820190915260018082527f310000000000000000000000000000000000000000000000000000000000000060209092019182526127eb9161010291614757565b505061020380547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555050505050505050565b60006101005446141561284c575060ff5490565b6129d9610101805461285d90614cb2565b80601f016020809104026020016040519081016040528092919081815260200182805461288990614cb2565b80156128d65780601f106128ab576101008083540402835291602001916128d6565b820191906000526020600020905b8154815290600101906020018083116128b957829003601f168201915b505050505061010280546128e990614cb2565b80601f016020809104026020016040519081016040528092919081815260200182805461291590614cb2565b80156129625780601f1061293757610100808354040283529160200191612962565b820191906000526020600020905b81548152906001019060200180831161294557829003601f168201915b50505050508151602092830120815191830191909120604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818601528082019390935260608301919091524660808301523060a0808401919091528151808403909101815260c09092019052805191012090565b905090565b6033546001600160a01b03163314612a5e5760405162461bcd60e51b815260206004820152602260248201527f5061757361626c653a2063616c6c6572206973206e6f7420746865207061757360448201527f65720000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b603380547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b1790556040517f6985a02210a168e66602d3235cb6db0e70f92b3ba4d376a33c0f3d9434bff62590600090a1565b6102008054610a8c90614cb2565b603354600090600160a01b900460ff1615612b155760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b3360008181526067602052604090205415612b805760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038416600090815260676020526040902054849015612bf65760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b610c4e338686613c82565b603354600090600160a01b900460ff1615612c515760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b3360008181526067602052604090205415612cbc5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038416600090815260676020526040902054849015612d325760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b610c4e338686613691565b609a546001600160a01b03163314612dbc5760405162461bcd60e51b8152602060048201526024808201527f526573637561626c653a2063616c6c6572206973206e6f74207468652072657360448201527f63756572000000000000000000000000000000000000000000000000000000006064820152608401610b59565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301526024820183905284169063a9059cbb906044016020604051808303816000875af1158015612e24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e489190614d64565b50505050565b603354600160a01b900460ff1615612e9b5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b6001600160a01b038716600090815260676020526040902054879015612f115760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038716600090815260676020526040902054879015612f875760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b612f9689898989898989613d2e565b505050505050505050565b603354600160a01b900460ff1615612fee5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b6001600160a01b0389166000908152606760205260409020548990156130645760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0389166000908152606760205260409020548990156130da5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6130eb8b8b8b8b8b8b8b8b8b613e99565b5050505050505050505050565b6102018054610a8c90614cb2565b6066546001600160a01b031633146131865760405162461bcd60e51b815260206004820152602c60248201527f426c6f636b6c69737461626c653a2063616c6c6572206973206e6f742074686560448201527f20626c6f636b6c697374657200000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b03811660008181526067602052604080822060019055517f917c251bb231c4b997a420bebe47edad5c20e70715da16c38e9b2e172e44ab929190a250565b603354600160a01b900460ff16156132185760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b6001600160a01b03891660009081526067602052604090205489901561328e5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0389166000908152606760205260409020548990156133045760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6130eb8b8b8b8b8b8b8b8b8b613faa565b336133286000546001600160a01b031690565b6001600160a01b03161461337e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b0381166133fa5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b59565b61136a81613c25565b336134166000546001600160a01b031690565b6001600160a01b03161461346c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b0381166134e85760405162461bcd60e51b815260206004820152603260248201527f426c6f636b6c69737461626c653a206e657720626c6f636b6c6973746572206960448201527f7320746865207a65726f206164647265737300000000000000000000000000006064820152608401610b59565b6066805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040517f68f10ceb42d30acc930aaaedf5b94559e14fc4f22496dc2c1b38b1b1b5231f9890600090a250565b6001600160a01b0383166135bb5760405162461bcd60e51b815260206004820152602860248201527f46696174546f6b656e3a20617070726f76652066726f6d20746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0382166136375760405162461bcd60e51b815260206004820152602660248201527f46696174546f6b656e3a20617070726f766520746f20746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b038381166000818152610205602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101611c7c565b6001600160a01b03831661370d5760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a207472616e736665722066726f6d20746865207a657260448201527f6f206164647265737300000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0382166137895760405162461bcd60e51b815260206004820152602760248201527f46696174546f6b656e3a207472616e7366657220746f20746865207a65726f2060448201527f61646472657373000000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b03831660009081526102046020526040902054808211156138195760405162461bcd60e51b815260206004820152602a60248201527f46696174546f6b656e3a207472616e7366657220616d6f756e7420657863656560448201527f64732062616c616e6365000000000000000000000000000000000000000000006064820152608401610b59565b6138238282614d35565b6001600160a01b03808616600090815261020460205260408082209390935590851681522054613854908390614d4c565b6001600160a01b038085166000818152610204602052604090819020939093559151908616907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906138a99086815260200190565b60405180910390a350505050565b336138ca6000546001600160a01b031690565b6001600160a01b03161461136a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff161561395857613953836140a0565b505050565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156139b2575060408051601f3d908101601f191682019092526139af91810190614d86565b60015b613a245760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f7420555550530000000000000000000000000000000000006064820152608401610b59565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114613ab95760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c655555494400000000000000000000000000000000000000000000006064820152608401610b59565b50613953838383614162565b6001600160a01b03808416600090815261020560209081526040808320938616835292905220546139539084908490613aff908590614d4c565b61353f565b613b0e8585614187565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b038716818301819052606080830188905283518084039091018152608090920190925290613b77613b6e612838565b8686868661421f565b6001600160a01b031614613bcd5760405162461bcd60e51b815260206004820152601a60248201527f454950333030393a20696e76616c6964207369676e61747572650000000000006044820152606401610b59565b6001600160a01b03861660008181526101356020908152604080832089845290915280822060019055518792917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a3505050505050565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038084166000908152610205602090815260408083209386168352929052205480821115613d1f5760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a2064656372656173656420616c6c6f77616e6365206260448201527f656c6f77207a65726f00000000000000000000000000000000000000000000006064820152608401610b59565b612e488484613aff8585614d35565b42841015613d7e5760405162461bcd60e51b815260206004820152601a60248201527f454950323631323a207065726d697420697320657870697265640000000000006044820152606401610b59565b6001600160a01b03871660009081526101686020526040812080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918a918a918a919086613dcc83614d9f565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040529050876001600160a01b0316613e2e613b6e612838565b6001600160a01b031614613e845760405162461bcd60e51b815260206004820152601a60248201527f454950323631323a20696e76616c6964207369676e61747572650000000000006044820152606401610b59565b613e8f88888861353f565b5050505050505050565b613ea58985888861429c565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760208201526001600160a01b03808c169282019290925290891660608201526080810188905260a0810187905260c0810186905260e08101859052600090610100015b6040516020818303038152906040529050896001600160a01b0316613f33613b6e612838565b6001600160a01b031614613f895760405162461bcd60e51b815260206004820152601a60248201527f454950333030393a20696e76616c6964207369676e61747572650000000000006044820152606401610b59565b613f938a86614390565b613f9e8a8a8a613691565b50505050505050505050565b6001600160a01b03881633146140285760405162461bcd60e51b815260206004820152602160248201527f454950333030393a2063616c6c6572206d75737420626520746865207061796560448201527f65000000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b6140348985888861429c565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860208201526001600160a01b03808c169282019290925290891660608201526080810188905260a0810187905260c0810186905260e0810185905260009061010001613f0d565b803b6141145760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e7472616374000000000000000000000000000000000000006064820152608401610b59565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b61416b836143e4565b6000825111806141785750805b1561395357612e488383614424565b6001600160a01b0382166000908152610135602090815260408083208484529091529020541561208e5760405162461bcd60e51b815260206004820152602a60248201527f454950333030393a20617574686f72697a6174696f6e2069732075736564206f60448201527f722063616e63656c6564000000000000000000000000000000000000000000006064820152608401610b59565b60008086838051906020012060405160200161426d9291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b60405160208183030381529060405280519060200120905061429181878787614450565b979650505050505050565b8142116143115760405162461bcd60e51b815260206004820152602760248201527f454950333030393a20617574686f72697a6174696f6e206973206e6f7420796560448201527f742076616c6964000000000000000000000000000000000000000000000000006064820152608401610b59565b8042106143865760405162461bcd60e51b815260206004820152602160248201527f454950333030393a20617574686f72697a6174696f6e2069732065787069726560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b612e488484614187565b6001600160a01b03821660008181526101356020908152604080832085845290915280822060019055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a35050565b6143ed816140a0565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606144498383604051806060016040528060278152602001614dd760279139614633565b9392505050565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156144e85760405162461bcd60e51b815260206004820152602660248201527f45435265636f7665723a20696e76616c6964207369676e61747572652027732760448201527f2076616c756500000000000000000000000000000000000000000000000000006064820152608401610b59565b8360ff16601b1415801561450057508360ff16601c14155b156145735760405162461bcd60e51b815260206004820152602660248201527f45435265636f7665723a20696e76616c6964207369676e61747572652027762760448201527f2076616c756500000000000000000000000000000000000000000000000000006064820152608401610b59565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa1580156145c7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661462a5760405162461bcd60e51b815260206004820152601c60248201527f45435265636f7665723a20696e76616c6964207369676e6174757265000000006044820152606401610b59565b95945050505050565b6060833b6146a95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610b59565b600080856001600160a01b0316856040516146c49190614dba565b600060405180830381855af49150503d80600081146146ff576040519150601f19603f3d011682016040523d82523d6000602084013e614704565b606091505b509150915061471482828661471e565b9695505050505050565b6060831561472d575081614449565b82511561473d5782518084602001fd5b8160405162461bcd60e51b8152600401610b59919061481c565b82805461476390614cb2565b90600052602060002090601f01602090048101928261478557600085556147cb565b82601f1061479e57805160ff19168380011785556147cb565b828001600101855582156147cb579182015b828111156147cb5782518255916020019190600101906147b0565b506147d79291506147db565b5090565b5b808211156147d757600081556001016147dc565b60005b8381101561480b5781810151838201526020016147f3565b83811115612e485750506000910152565b602081526000825180602084015261483b8160408501602087016147f0565b601f01601f19169190910160400192915050565b6001600160a01b038116811461136a57600080fd5b80356111068161484f565b6000806040838503121561488257600080fd5b823561488d8161484f565b946020939093013593505050565b6000806000606084860312156148b057600080fd5b83356148bb8161484f565b925060208401356148cb8161484f565b929592945050506040919091013590565b6000602082840312156148ee57600080fd5b81356144498161484f565b60006020828403121561490b57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff8084111561495c5761495c614912565b604051601f8501601f19908116603f0116810190828211818310171561498457614984614912565b8160405280935085815286868601111561499d57600080fd5b858560208301376000602087830101525050509392505050565b600080604083850312156149ca57600080fd5b82356149d58161484f565b9150602083013567ffffffffffffffff8111156149f157600080fd5b8301601f81018513614a0257600080fd5b614a1185823560208401614941565b9150509250929050565b803560ff8116811461110657600080fd5b600080600080600060a08688031215614a4457600080fd5b8535614a4f8161484f565b945060208601359350614a6460408701614a1b565b94979396509394606081013594506080013592915050565b600082601f830112614a8d57600080fd5b61444983833560208501614941565b60008060008060008060008060006101208a8c031215614abb57600080fd5b893567ffffffffffffffff80821115614ad357600080fd5b614adf8d838e01614a7c565b9a5060208c0135915080821115614af557600080fd5b614b018d838e01614a7c565b995060408c0135915080821115614b1757600080fd5b50614b248c828d01614a7c565b975050614b3360608b01614a1b565b9550614b4160808b01614864565b9450614b4f60a08b01614864565b9350614b5d60c08b01614864565b9250614b6b60e08b01614864565b9150614b7a6101008b01614864565b90509295985092959850929598565b600080600080600080600060e0888a031215614ba457600080fd5b8735614baf8161484f565b96506020880135614bbf8161484f565b95506040880135945060608801359350614bdb60808901614a1b565b925060a0880135915060c0880135905092959891949750929550565b60008060408385031215614c0a57600080fd5b8235614c158161484f565b91506020830135614c258161484f565b809150509250929050565b60008060008060008060008060006101208a8c031215614c4f57600080fd5b8935614c5a8161484f565b985060208a0135614c6a8161484f565b975060408a0135965060608a0135955060808a0135945060a08a01359350614c9460c08b01614a1b565b925060e08a013591506101008a013590509295985092959850929598565b600181811c90821680614cc657607f821691505b60208210811415614d00577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015614d4757614d47614d06565b500390565b60008219821115614d5f57614d5f614d06565b500190565b600060208284031215614d7657600080fd5b8151801515811461444957600080fd5b600060208284031215614d9857600080fd5b5051919050565b6000600019821415614db357614db3614d06565b5060010190565b60008251614dcc8184602087016147f0565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122088b0c5194b9eca0801c00f098997e6c9042e68a8d796e28173d6611567e0d9a464736f6c634300080b0033", + "deployedBytecode": "0x6080604052600436106103295760003560e01c80637ecebe00116101a5578063aa271e1a116100ec578063e3ee160e11610095578063e94a01021161006f578063e94a0102146109d7578063ef55bec614610a1e578063f2fde38b14610a3e578063f9b5aa9214610a5e57600080fd5b8063e3ee160e14610982578063e5a6b10f146109a2578063e5c7160b146109b757600080fd5b8063d505accf116100c6578063d505accf146108e7578063d916948714610907578063dd62ed3e1461093b57600080fd5b8063aa271e1a1461086d578063b2118a8d146108a7578063b54d9497146108c757600080fd5b806395d89b411161014e578063a457c2d711610128578063a457c2d71461080c578063a754d48f1461082c578063a9059cbb1461084d57600080fd5b806395d89b41146107a35780639fd0506d146107b8578063a0cc6a68146107d857600080fd5b80638a6db9c31161017f5780638a6db9c3146107155780638da5cb5b1461074c5780638e204c431461076a57600080fd5b80637ecebe00146106955780637f2eecc3146106cc5780638456cb591461070057600080fd5b80633f4ba83a1161027457806352d1902d1161021d5780635c975abb116101f75780635c975abb1461060857806370a082311461062957806374ebf673146106605780637b134b4c1461068057600080fd5b806352d1902d146105b3578063554bab3c146105c85780635a049a70146105e857600080fd5b8063439531fd1161024e578063439531fd146105605780634e44d956146105805780634f1ef286146105a057600080fd5b80633f4ba83a1461050b57806340c10f191461052057806342966c681461054057600080fd5b806330adf81f116102d65780633659cfe6116102b05780633659cfe61461049357806338a63183146104b357806339509351146104eb57600080fd5b806330adf81f1461040b578063313ce5671461043f57806331b230201461047357600080fd5b806323b872dd1161030757806323b872dd146103a95780632ab60045146103c95780633092afd5146103eb57600080fd5b806306fdde031461032e578063095ea7b31461035957806318160ddd14610389575b600080fd5b34801561033a57600080fd5b50610343610a7e565b604051610350919061481c565b60405180910390f35b34801561036557600080fd5b5061037961037436600461486f565b610b0d565b6040519015158152602001610350565b34801561039557600080fd5b50610202545b604051908152602001610350565b3480156103b557600080fd5b506103796103c436600461489b565b610c59565b3480156103d557600080fd5b506103e96103e43660046148dc565b610eed565b005b3480156103f757600080fd5b506103796104063660046148dc565b611029565b34801561041757600080fd5b5061039b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b34801561044b57600080fd5b506102035461046190600160a01b900460ff1681565b60405160ff9091168152602001610350565b34801561047f57600080fd5b506103e961048e3660046148dc565b61110b565b34801561049f57600080fd5b506103e96104ae3660046148dc565b6111cf565b3480156104bf57600080fd5b50609a546104d3906001600160a01b031681565b6040516001600160a01b039091168152602001610350565b3480156104f757600080fd5b5061037961050636600461486f565b61136d565b34801561051757600080fd5b506103e96114a9565b34801561052c57600080fd5b5061037961053b36600461486f565b61157c565b34801561054c57600080fd5b506103e961055b3660046148f9565b6119a7565b34801561056c57600080fd5b506103e961057b3660046148dc565b611c89565b34801561058c57600080fd5b5061037961059b36600461486f565b611dc6565b6103e96105ae3660046149b7565b611f03565b3480156105bf57600080fd5b5061039b612092565b3480156105d457600080fd5b506103e96105e33660046148dc565b612157565b3480156105f457600080fd5b506103e9610603366004614a2c565b612293565b34801561061457600080fd5b5060335461037990600160a01b900460ff1681565b34801561063557600080fd5b5061039b6106443660046148dc565b6001600160a01b03166000908152610204602052604090205490565b34801561066c57600080fd5b506103e961067b366004614a9c565b6122f4565b34801561068c57600080fd5b5061039b612838565b3480156106a157600080fd5b5061039b6106b03660046148dc565b6001600160a01b03166000908152610168602052604090205490565b3480156106d857600080fd5b5061039b7fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b34801561070c57600080fd5b506103e96129de565b34801561072157600080fd5b5061039b6107303660046148dc565b6001600160a01b03166000908152610207602052604090205490565b34801561075857600080fd5b506000546001600160a01b03166104d3565b34801561077657600080fd5b506103796107853660046148dc565b6001600160a01b031660009081526067602052604090205460011490565b3480156107af57600080fd5b50610343612ab7565b3480156107c457600080fd5b506033546104d3906001600160a01b031681565b3480156107e457600080fd5b5061039b7f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b34801561081857600080fd5b5061037961082736600461486f565b612ac5565b34801561083857600080fd5b50610203546104d3906001600160a01b031681565b34801561085957600080fd5b5061037961086836600461486f565b612c01565b34801561087957600080fd5b506103796108883660046148dc565b6001600160a01b03166000908152610206602052604090205460ff1690565b3480156108b357600080fd5b506103e96108c236600461489b565b612d3d565b3480156108d357600080fd5b506066546104d3906001600160a01b031681565b3480156108f357600080fd5b506103e9610902366004614b89565b612e4e565b34801561091357600080fd5b5061039b7f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b34801561094757600080fd5b5061039b610956366004614bf7565b6001600160a01b0391821660009081526102056020908152604080832093909416825291909152205490565b34801561098e57600080fd5b506103e961099d366004614c30565b612fa1565b3480156109ae57600080fd5b506103436130f8565b3480156109c357600080fd5b506103e96109d23660046148dc565b613106565b3480156109e357600080fd5b506103796109f236600461486f565b6001600160a01b0391909116600090815261013560209081526040808320938352929052205460011490565b348015610a2a57600080fd5b506103e9610a39366004614c30565b6131cb565b348015610a4a57600080fd5b506103e9610a593660046148dc565b613315565b348015610a6a57600080fd5b506103e9610a793660046148dc565b613403565b6101ff8054610a8c90614cb2565b80601f0160208091040260200160405190810160405280929190818152602001828054610ab890614cb2565b8015610b055780601f10610ada57610100808354040283529160200191610b05565b820191906000526020600020905b815481529060010190602001808311610ae857829003601f168201915b505050505081565b603354600090600160a01b900460ff1615610b625760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b3360008181526067602052604090205415610bcd5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038416600090815260676020526040902054849015610c435760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b610c4e33868661353f565b506001949350505050565b603354600090600160a01b900460ff1615610ca95760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b3360008181526067602052604090205415610d145760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038516600090815260676020526040902054859015610d8a5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038516600090815260676020526040902054859015610e005760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0387166000908152610205602090815260408083203384529091529020546000198114610ed45785811015610ea45760405162461bcd60e51b815260206004820152602c60248201527f46696174546f6b656e3a207472616e7366657220616d6f756e7420657863656560448201527f647320616c6c6f77616e636500000000000000000000000000000000000000006064820152608401610b59565b610eae8682614d35565b6001600160a01b0389166000908152610205602090815260408083203384529091529020555b610edf888888613691565b506001979650505050505050565b33610f006000546001600160a01b031690565b6001600160a01b031614610f565760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b038116610fd25760405162461bcd60e51b815260206004820152602a60248201527f526573637561626c653a206e6577207265736375657220697320746865207a6560448201527f726f2061646472657373000000000000000000000000000000000000000000006064820152608401610b59565b609a805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040517fe475e580d85111348e40d8ca33cfdd74c30fe1655c2d8537a13abc10065ffa5a90600090a250565b610203546000906001600160a01b031633146110ad5760405162461bcd60e51b815260206004820152602860248201527f46696174546f6b656e3a2063616c6c6572206973206e6f7420746865206d696e60448201527f74657241646d696e0000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b038216600081815261020660209081526040808320805460ff19169055610207909152808220829055517fe94479a9f7e1952cc78f2d6baab678adc1b772d936c6583def489e524cb666929190a25060015b919050565b6066546001600160a01b0316331461118b5760405162461bcd60e51b815260206004820152602c60248201527f426c6f636b6c69737461626c653a2063616c6c6572206973206e6f742074686560448201527f20626c6f636b6c697374657200000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b038116600081815260676020526040808220829055517fbc3fe0fc667d12a7a22748747f024a7d971127ffc48f6622675d3e97a2591a519190a250565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016141561126e5760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610b59565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166112c97f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b0316146113455760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610b59565b61134e816138b7565b6040805160008082526020820190925261136a91839190613920565b50565b603354600090600160a01b900460ff16156113bd5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b33600081815260676020526040902054156114285760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b03841660009081526067602052604090205484901561149e5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b610c4e338686613ac5565b6033546001600160a01b031633146115295760405162461bcd60e51b815260206004820152602260248201527f5061757361626c653a2063616c6c6572206973206e6f7420746865207061757360448201527f65720000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b603380547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f7805862f689e2f13df9f062ff482ad3ad112aca9e0847911ed832e158c525b3390600090a1565b603354600090600160a01b900460ff16156115cc5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b336000908152610206602052604090205460ff166116525760405162461bcd60e51b815260206004820152602160248201527f46696174546f6b656e3a2063616c6c6572206973206e6f742061206d696e746560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b33600081815260676020526040902054156116bd5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0384166000908152606760205260409020548490156117335760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0385166117af5760405162461bcd60e51b815260206004820152602360248201527f46696174546f6b656e3a206d696e7420746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610b59565b600084116118255760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a206d696e7420616d6f756e74206e6f7420677265617460448201527f6572207468616e203000000000000000000000000000000000000000000000006064820152608401610b59565b3360009081526102076020526040902054808511156118ac5760405162461bcd60e51b815260206004820152602e60248201527f46696174546f6b656e3a206d696e7420616d6f756e742065786365656473206d60448201527f696e746572416c6c6f77616e63650000000000000000000000000000000000006064820152608401610b59565b84610202546118bb9190614d4c565b610202556001600160a01b038616600090815261020460205260409020546118e4908690614d4c565b6001600160a01b038716600090815261020460205260409020556119088582614d35565b336000818152610207602090815260409182902093909355518781526001600160a01b038916927fab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8910160405180910390a36040518581526001600160a01b038716906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a350600195945050505050565b603354600160a01b900460ff16156119f45760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b336000908152610206602052604090205460ff16611a7a5760405162461bcd60e51b815260206004820152602160248201527f46696174546f6b656e3a2063616c6c6572206973206e6f742061206d696e746560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b3360008181526067602052604090205415611ae55760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b336000908152610204602052604090205482611b695760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a206275726e20616d6f756e74206e6f7420677265617460448201527f6572207468616e203000000000000000000000000000000000000000000000006064820152608401610b59565b82811015611bdf5760405162461bcd60e51b815260206004820152602660248201527f46696174546f6b656e3a206275726e20616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610b59565b8261020254611bee9190614d35565b61020255611bfc8382614d35565b3360008181526102046020526040908190209290925590517fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca590611c439086815260200190565b60405180910390a260405183815260009033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a3505050565b33611c9c6000546001600160a01b031690565b6001600160a01b031614611cf25760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b038116611d6e5760405162461bcd60e51b815260206004820152602e60248201527f46696174546f6b656e3a206e6577206d696e74657241646d696e20697320746860448201527f65207a65726f20616464726573730000000000000000000000000000000000006064820152608401610b59565b610203805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040517f4e6db312b79f0cdadbee5f76e2473786c1e81cba2356eacccc2aa5b5e6e3664c90600090a250565b603354600090600160a01b900460ff1615611e165760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b610203546001600160a01b03163314611e975760405162461bcd60e51b815260206004820152602860248201527f46696174546f6b656e3a2063616c6c6572206973206e6f7420746865206d696e60448201527f74657241646d696e0000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b038316600081815261020660209081526040808320805460ff1916600117905561020782529182902085905590518481527f46980fca912ef9bcdbd36877427b6b90e860769f604e89c0e67720cece530d20910160405180910390a250600192915050565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161415611fa25760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152608401610b59565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611ffd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6001600160a01b0316146120795760405162461bcd60e51b815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152608401610b59565b612082826138b7565b61208e82826001613920565b5050565b6000306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146121325760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152608401610b59565b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b3361216a6000546001600160a01b031690565b6001600160a01b0316146121c05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b03811661223c5760405162461bcd60e51b815260206004820152602860248201527f5061757361626c653a206e65772070617573657220697320746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401610b59565b6033805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040517fb80482a293ca2e013eda8683c9bd7fc8347cfdaeea5ede58cba46df502c2a60490600090a250565b603354600160a01b900460ff16156122e05760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b6122ed8585858585613b04565b5050505050565b610203547501000000000000000000000000000000000000000000900460ff16156123875760405162461bcd60e51b815260206004820152602a60248201527f46696174546f6b656e3a20636f6e747261637420697320616c7265616479206960448201527f6e697469616c697a6564000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0385166124035760405162461bcd60e51b815260206004820152602e60248201527f46696174546f6b656e3a206e6577206d696e74657241646d696e20697320746860448201527f65207a65726f20616464726573730000000000000000000000000000000000006064820152608401610b59565b6001600160a01b03841661247f5760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a206e65772070617573657220697320746865207a657260448201527f6f206164647265737300000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0383166124fb5760405162461bcd60e51b815260206004820152602e60248201527f46696174546f6b656e3a206e657720626c6f636b6c697374657220697320746860448201527f65207a65726f20616464726573730000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0382166125775760405162461bcd60e51b815260206004820152602a60248201527f46696174546f6b656e3a206e6577207265736375657220697320746865207a6560448201527f726f2061646472657373000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0381166125f35760405162461bcd60e51b815260206004820152602860248201527f46696174546f6b656e3a206e6577206f776e657220697320746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401610b59565b8851612607906101ff9060208c0190614757565b50875161261c906102009060208b0190614757565b508651612631906102019060208a0190614757565b5061020380547fffffffffffffffffffffff00000000000000000000000000000000000000000016600160a01b60ff89160273ffffffffffffffffffffffffffffffffffffffff19908116919091176001600160a01b0388811691909117909255603380548216878416179055606680548216868416179055609a80549091169184169190911790556126c381613c25565b30600081815260676020908152604091829020600190819055825180840184529081527f3100000000000000000000000000000000000000000000000000000000000000908201528b518c82012082517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81840152808401919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015246608082015260a0808201949094528251808203909401845260c001909152815191012060ff55466101005588516127a5906101019060208c0190614757565b506040805180820190915260018082527f310000000000000000000000000000000000000000000000000000000000000060209092019182526127eb9161010291614757565b505061020380547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555050505050505050565b60006101005446141561284c575060ff5490565b6129d9610101805461285d90614cb2565b80601f016020809104026020016040519081016040528092919081815260200182805461288990614cb2565b80156128d65780601f106128ab576101008083540402835291602001916128d6565b820191906000526020600020905b8154815290600101906020018083116128b957829003601f168201915b505050505061010280546128e990614cb2565b80601f016020809104026020016040519081016040528092919081815260200182805461291590614cb2565b80156129625780601f1061293757610100808354040283529160200191612962565b820191906000526020600020905b81548152906001019060200180831161294557829003601f168201915b50505050508151602092830120815191830191909120604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818601528082019390935260608301919091524660808301523060a0808401919091528151808403909101815260c09092019052805191012090565b905090565b6033546001600160a01b03163314612a5e5760405162461bcd60e51b815260206004820152602260248201527f5061757361626c653a2063616c6c6572206973206e6f7420746865207061757360448201527f65720000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b603380547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b1790556040517f6985a02210a168e66602d3235cb6db0e70f92b3ba4d376a33c0f3d9434bff62590600090a1565b6102008054610a8c90614cb2565b603354600090600160a01b900460ff1615612b155760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b3360008181526067602052604090205415612b805760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038416600090815260676020526040902054849015612bf65760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b610c4e338686613c82565b603354600090600160a01b900460ff1615612c515760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b3360008181526067602052604090205415612cbc5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038416600090815260676020526040902054849015612d325760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b610c4e338686613691565b609a546001600160a01b03163314612dbc5760405162461bcd60e51b8152602060048201526024808201527f526573637561626c653a2063616c6c6572206973206e6f74207468652072657360448201527f63756572000000000000000000000000000000000000000000000000000000006064820152608401610b59565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301526024820183905284169063a9059cbb906044016020604051808303816000875af1158015612e24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e489190614d64565b50505050565b603354600160a01b900460ff1615612e9b5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b6001600160a01b038716600090815260676020526040902054879015612f115760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b038716600090815260676020526040902054879015612f875760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b612f9689898989898989613d2e565b505050505050505050565b603354600160a01b900460ff1615612fee5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b6001600160a01b0389166000908152606760205260409020548990156130645760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0389166000908152606760205260409020548990156130da5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6130eb8b8b8b8b8b8b8b8b8b613e99565b5050505050505050505050565b6102018054610a8c90614cb2565b6066546001600160a01b031633146131865760405162461bcd60e51b815260206004820152602c60248201527f426c6f636b6c69737461626c653a2063616c6c6572206973206e6f742074686560448201527f20626c6f636b6c697374657200000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b03811660008181526067602052604080822060019055517f917c251bb231c4b997a420bebe47edad5c20e70715da16c38e9b2e172e44ab929190a250565b603354600160a01b900460ff16156132185760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610b59565b6001600160a01b03891660009081526067602052604090205489901561328e5760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6001600160a01b0389166000908152606760205260409020548990156133045760405162461bcd60e51b815260206004820152602560248201527f426c6f636b6c69737461626c653a206163636f756e7420697320626c6f636b6c6044820152641a5cdd195960da1b6064820152608401610b59565b6130eb8b8b8b8b8b8b8b8b8b613faa565b336133286000546001600160a01b031690565b6001600160a01b03161461337e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b0381166133fa5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b59565b61136a81613c25565b336134166000546001600160a01b031690565b6001600160a01b03161461346c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b6001600160a01b0381166134e85760405162461bcd60e51b815260206004820152603260248201527f426c6f636b6c69737461626c653a206e657720626c6f636b6c6973746572206960448201527f7320746865207a65726f206164647265737300000000000000000000000000006064820152608401610b59565b6066805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040517f68f10ceb42d30acc930aaaedf5b94559e14fc4f22496dc2c1b38b1b1b5231f9890600090a250565b6001600160a01b0383166135bb5760405162461bcd60e51b815260206004820152602860248201527f46696174546f6b656e3a20617070726f76652066726f6d20746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0382166136375760405162461bcd60e51b815260206004820152602660248201527f46696174546f6b656e3a20617070726f766520746f20746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b038381166000818152610205602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101611c7c565b6001600160a01b03831661370d5760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a207472616e736665722066726f6d20746865207a657260448201527f6f206164647265737300000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b0382166137895760405162461bcd60e51b815260206004820152602760248201527f46696174546f6b656e3a207472616e7366657220746f20746865207a65726f2060448201527f61646472657373000000000000000000000000000000000000000000000000006064820152608401610b59565b6001600160a01b03831660009081526102046020526040902054808211156138195760405162461bcd60e51b815260206004820152602a60248201527f46696174546f6b656e3a207472616e7366657220616d6f756e7420657863656560448201527f64732062616c616e6365000000000000000000000000000000000000000000006064820152608401610b59565b6138238282614d35565b6001600160a01b03808616600090815261020460205260408082209390935590851681522054613854908390614d4c565b6001600160a01b038085166000818152610204602052604090819020939093559151908616907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906138a99086815260200190565b60405180910390a350505050565b336138ca6000546001600160a01b031690565b6001600160a01b03161461136a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b59565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff161561395857613953836140a0565b505050565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156139b2575060408051601f3d908101601f191682019092526139af91810190614d86565b60015b613a245760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f7420555550530000000000000000000000000000000000006064820152608401610b59565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8114613ab95760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c655555494400000000000000000000000000000000000000000000006064820152608401610b59565b50613953838383614162565b6001600160a01b03808416600090815261020560209081526040808320938616835292905220546139539084908490613aff908590614d4c565b61353f565b613b0e8585614187565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b038716818301819052606080830188905283518084039091018152608090920190925290613b77613b6e612838565b8686868661421f565b6001600160a01b031614613bcd5760405162461bcd60e51b815260206004820152601a60248201527f454950333030393a20696e76616c6964207369676e61747572650000000000006044820152606401610b59565b6001600160a01b03861660008181526101356020908152604080832089845290915280822060019055518792917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a3505050505050565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038084166000908152610205602090815260408083209386168352929052205480821115613d1f5760405162461bcd60e51b815260206004820152602960248201527f46696174546f6b656e3a2064656372656173656420616c6c6f77616e6365206260448201527f656c6f77207a65726f00000000000000000000000000000000000000000000006064820152608401610b59565b612e488484613aff8585614d35565b42841015613d7e5760405162461bcd60e51b815260206004820152601a60248201527f454950323631323a207065726d697420697320657870697265640000000000006044820152606401610b59565b6001600160a01b03871660009081526101686020526040812080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918a918a918a919086613dcc83614d9f565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040529050876001600160a01b0316613e2e613b6e612838565b6001600160a01b031614613e845760405162461bcd60e51b815260206004820152601a60248201527f454950323631323a20696e76616c6964207369676e61747572650000000000006044820152606401610b59565b613e8f88888861353f565b5050505050505050565b613ea58985888861429c565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760208201526001600160a01b03808c169282019290925290891660608201526080810188905260a0810187905260c0810186905260e08101859052600090610100015b6040516020818303038152906040529050896001600160a01b0316613f33613b6e612838565b6001600160a01b031614613f895760405162461bcd60e51b815260206004820152601a60248201527f454950333030393a20696e76616c6964207369676e61747572650000000000006044820152606401610b59565b613f938a86614390565b613f9e8a8a8a613691565b50505050505050505050565b6001600160a01b03881633146140285760405162461bcd60e51b815260206004820152602160248201527f454950333030393a2063616c6c6572206d75737420626520746865207061796560448201527f65000000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b6140348985888861429c565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860208201526001600160a01b03808c169282019290925290891660608201526080810188905260a0810187905260c0810186905260e0810185905260009061010001613f0d565b803b6141145760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e7472616374000000000000000000000000000000000000006064820152608401610b59565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b61416b836143e4565b6000825111806141785750805b1561395357612e488383614424565b6001600160a01b0382166000908152610135602090815260408083208484529091529020541561208e5760405162461bcd60e51b815260206004820152602a60248201527f454950333030393a20617574686f72697a6174696f6e2069732075736564206f60448201527f722063616e63656c6564000000000000000000000000000000000000000000006064820152608401610b59565b60008086838051906020012060405160200161426d9291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b60405160208183030381529060405280519060200120905061429181878787614450565b979650505050505050565b8142116143115760405162461bcd60e51b815260206004820152602760248201527f454950333030393a20617574686f72697a6174696f6e206973206e6f7420796560448201527f742076616c6964000000000000000000000000000000000000000000000000006064820152608401610b59565b8042106143865760405162461bcd60e51b815260206004820152602160248201527f454950333030393a20617574686f72697a6174696f6e2069732065787069726560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610b59565b612e488484614187565b6001600160a01b03821660008181526101356020908152604080832085845290915280822060019055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a35050565b6143ed816140a0565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606144498383604051806060016040528060278152602001614dd760279139614633565b9392505050565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156144e85760405162461bcd60e51b815260206004820152602660248201527f45435265636f7665723a20696e76616c6964207369676e61747572652027732760448201527f2076616c756500000000000000000000000000000000000000000000000000006064820152608401610b59565b8360ff16601b1415801561450057508360ff16601c14155b156145735760405162461bcd60e51b815260206004820152602660248201527f45435265636f7665723a20696e76616c6964207369676e61747572652027762760448201527f2076616c756500000000000000000000000000000000000000000000000000006064820152608401610b59565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa1580156145c7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661462a5760405162461bcd60e51b815260206004820152601c60248201527f45435265636f7665723a20696e76616c6964207369676e6174757265000000006044820152606401610b59565b95945050505050565b6060833b6146a95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610b59565b600080856001600160a01b0316856040516146c49190614dba565b600060405180830381855af49150503d80600081146146ff576040519150601f19603f3d011682016040523d82523d6000602084013e614704565b606091505b509150915061471482828661471e565b9695505050505050565b6060831561472d575081614449565b82511561473d5782518084602001fd5b8160405162461bcd60e51b8152600401610b59919061481c565b82805461476390614cb2565b90600052602060002090601f01602090048101928261478557600085556147cb565b82601f1061479e57805160ff19168380011785556147cb565b828001600101855582156147cb579182015b828111156147cb5782518255916020019190600101906147b0565b506147d79291506147db565b5090565b5b808211156147d757600081556001016147dc565b60005b8381101561480b5781810151838201526020016147f3565b83811115612e485750506000910152565b602081526000825180602084015261483b8160408501602087016147f0565b601f01601f19169190910160400192915050565b6001600160a01b038116811461136a57600080fd5b80356111068161484f565b6000806040838503121561488257600080fd5b823561488d8161484f565b946020939093013593505050565b6000806000606084860312156148b057600080fd5b83356148bb8161484f565b925060208401356148cb8161484f565b929592945050506040919091013590565b6000602082840312156148ee57600080fd5b81356144498161484f565b60006020828403121561490b57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff8084111561495c5761495c614912565b604051601f8501601f19908116603f0116810190828211818310171561498457614984614912565b8160405280935085815286868601111561499d57600080fd5b858560208301376000602087830101525050509392505050565b600080604083850312156149ca57600080fd5b82356149d58161484f565b9150602083013567ffffffffffffffff8111156149f157600080fd5b8301601f81018513614a0257600080fd5b614a1185823560208401614941565b9150509250929050565b803560ff8116811461110657600080fd5b600080600080600060a08688031215614a4457600080fd5b8535614a4f8161484f565b945060208601359350614a6460408701614a1b565b94979396509394606081013594506080013592915050565b600082601f830112614a8d57600080fd5b61444983833560208501614941565b60008060008060008060008060006101208a8c031215614abb57600080fd5b893567ffffffffffffffff80821115614ad357600080fd5b614adf8d838e01614a7c565b9a5060208c0135915080821115614af557600080fd5b614b018d838e01614a7c565b995060408c0135915080821115614b1757600080fd5b50614b248c828d01614a7c565b975050614b3360608b01614a1b565b9550614b4160808b01614864565b9450614b4f60a08b01614864565b9350614b5d60c08b01614864565b9250614b6b60e08b01614864565b9150614b7a6101008b01614864565b90509295985092959850929598565b600080600080600080600060e0888a031215614ba457600080fd5b8735614baf8161484f565b96506020880135614bbf8161484f565b95506040880135945060608801359350614bdb60808901614a1b565b925060a0880135915060c0880135905092959891949750929550565b60008060408385031215614c0a57600080fd5b8235614c158161484f565b91506020830135614c258161484f565b809150509250929050565b60008060008060008060008060006101208a8c031215614c4f57600080fd5b8935614c5a8161484f565b985060208a0135614c6a8161484f565b975060408a0135965060608a0135955060808a0135945060a08a01359350614c9460c08b01614a1b565b925060e08a013591506101008a013590509295985092959850929598565b600181811c90821680614cc657607f821691505b60208210811415614d00577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015614d4757614d47614d06565b500390565b60008219821115614d5f57614d5f614d06565b500190565b600060208284031215614d7657600080fd5b8151801515811461444957600080fd5b600060208284031215614d9857600080fd5b5051919050565b6000600019821415614db357614db3614d06565b5060010190565b60008251614dcc8184602087016147f0565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122088b0c5194b9eca0801c00f098997e6c9042e68a8d796e28173d6611567e0d9a464736f6c634300080b0033", + "linkReferences": {}, + "deployedLinkReferences": {} + } \ No newline at end of file diff --git a/jpyc_sdk/client.py b/jpyc_sdk/client.py new file mode 100644 index 0000000..39502f8 --- /dev/null +++ b/jpyc_sdk/client.py @@ -0,0 +1,89 @@ +import os +from typing import Optional, Union +from web3 import Web3 +from eth_account import Account +from eth_account.signers.local import LocalAccount +from web3.middleware import SignAndSendRawMiddlewareBuilder + +from .interfaces.client import ISdkClient +from .utils.types import Address, ChainName, NetworkName, Endpoint +from .utils.chains import get_web3_for_chain, is_valid_chain_name, is_valid_network_name +from .utils.errors import InvalidChainNameError, InvalidNetworkNameError + +class SdkClient(ISdkClient): + """SDK client for interacting with JPYC tokens""" + + def __init__(self, chain_name: ChainName, network_name: NetworkName, rpc_endpoint: Endpoint): + """ + Initialize the SDK client. + + Args: + chain_name: Chain name (e.g., 'ethereum', 'polygon') + network_name: Network name (e.g., 'mainnet', 'goerli') + rpc_endpoint: RPC endpoint URL + + Raises: + InvalidChainNameError: If the chain name is not supported + InvalidNetworkNameError: If the network name is not supported for the chain + """ + if not is_valid_chain_name(chain_name): + raise InvalidChainNameError(chain_name) + if not is_valid_network_name(chain_name, network_name): + raise InvalidNetworkNameError(chain_name, network_name) + + self.chain_name = chain_name + self.network_name = network_name + self.rpc_endpoint = rpc_endpoint + self.web3 = get_web3_for_chain(chain_name, network_name, rpc_endpoint) + self.account = None + + def create_private_key_account(self, private_key: Optional[str] = None) -> Optional[LocalAccount]: + """ + Create an account from a private key. + + Args: + private_key: Private key (optional, will use environment variable if not specified) + + Returns: + LocalAccount: Account created from the private key + + Raises: + ValueError: If no private key is provided and none is found in environment variables + """ + pk = private_key or os.environ.get('PRIVATE_KEY') + if not pk: + raise ValueError("Private key not provided and not found in environment variables") + + self.account = Account.from_key(pk) + return self.account + + def create_local_client(self, account: Optional[Union[str, LocalAccount]] = None) -> Web3: + """ + Create a client from an account. + + Args: + account: Account to use (optional, will use the account created with create_private_key_account if not specified) + + Returns: + Web3: Web3 instance configured with the account + + Raises: + ValueError: If no account is provided and no account has been created with create_private_key_account + """ + if account is None: + if self.account is None: + raise ValueError("No account provided and no account created with create_private_key_account") + account = self.account + + if self.web3 is None: + self.web3 = Web3(Web3.HTTPProvider(self.rpc_endpoint)) + + if isinstance(account, str): + # Treat as address and use + self.web3.eth.default_account = Web3.to_checksum_address(account) + else: + # Set up middleware for account + self.web3.middleware_onion.inject(SignAndSendRawMiddlewareBuilder.build(account), layer=0) + self.web3.eth.default_account = account.address + + return self.web3 \ No newline at end of file diff --git a/jpyc_sdk/interfaces/__init__.py b/jpyc_sdk/interfaces/__init__.py new file mode 100644 index 0000000..5efd5d6 --- /dev/null +++ b/jpyc_sdk/interfaces/__init__.py @@ -0,0 +1,3 @@ +from .client import * +from .jpyc import * +from .abis import * \ No newline at end of file diff --git a/jpyc_sdk/interfaces/abis.py b/jpyc_sdk/interfaces/abis.py new file mode 100644 index 0000000..1b34504 --- /dev/null +++ b/jpyc_sdk/interfaces/abis.py @@ -0,0 +1,12 @@ +""" +ABI (Application Binary Interface) definitions for JPYC contracts +""" + +import json +import pathlib + +package_dir = pathlib.Path(__file__).parent.parent +abi_file = package_dir / "artifacts" / "JPYCv2" / "contracts" / "v1" / "FiatTokenV1.sol" / "FiatTokenV1.json" + +with open(abi_file, 'r') as f: + JPYC_V2_ABI = json.load(f)['abi'] \ No newline at end of file diff --git a/jpyc_sdk/interfaces/client.py b/jpyc_sdk/interfaces/client.py new file mode 100644 index 0000000..f48d919 --- /dev/null +++ b/jpyc_sdk/interfaces/client.py @@ -0,0 +1,38 @@ +""" +Interface definitions for the SDK client +""" + +from typing import Protocol, Optional, Any +from eth_account.signers.local import LocalAccount +from web3 import Web3 + +class ISdkClient(Protocol): + """ + Interface for the SDK client + + This interface defines the methods required for SDK client operations. + """ + + def create_private_key_account(self, private_key: Optional[str] = None) -> Optional[LocalAccount]: + """ + Create an account from a private key + + Args: + private_key: Private key (optional) + + Returns: + LocalAccount: Created account + """ + ... + + def create_local_client(self, account: Optional[Any] = None) -> Web3: + """ + Create a local client configured with an account + + Args: + account: Account to use (optional) + + Returns: + Web3: Configured Web3 instance + """ + ... \ No newline at end of file diff --git a/jpyc_sdk/interfaces/jpyc.py b/jpyc_sdk/interfaces/jpyc.py new file mode 100644 index 0000000..973436b --- /dev/null +++ b/jpyc_sdk/interfaces/jpyc.py @@ -0,0 +1,262 @@ +""" +Interface definitions for JPYC token operations +""" + +from typing import Protocol, Union +from hexbytes import HexBytes + +from ..utils.types import Address, Bytes32, Uint256, Uint8 + +class IJPYC(Protocol): + """ + Interface for JPYC token operations + + This interface defines the methods required to interact with + the JPYC stablecoin. + """ + + # View関数 + + async def is_minter(self, account: Address) -> bool: + """ + Check if an address is a minter + + Args: + account: Address to check + + Returns: + bool: True if the address is a minter, False otherwise + """ + ... + + async def minter_allowance(self, minter: Address) -> float: + """ + Get the minting allowance for a minter + + Args: + minter: Minter address + + Returns: + int: Minting allowance amount + """ + ... + + async def total_supply(self) -> float: + """ + Get the total token supply + + Returns: + int: Total supply amount + """ + ... + + async def balance_of(self, account: Address) -> float: + """ + Get the token balance of an account + + Args: + account: Account address + + Returns: + int: Account balance + """ + ... + + async def allowance(self, owner: Address, spender: Address) -> float: + """ + Get the spending allowance for a spender + + Args: + owner: Token owner address + spender: Spender address + + Returns: + int: Allowance amount + """ + ... + + async def nonces(self, owner: Address) -> int: + """ + Get the current nonce for an address (used for EIP-2612 permits) + + Args: + owner: Owner address + + Returns: + int: Current nonce + """ + ... + + # トランザクション関数 + + async def configure_minter(self, minter: Address, minter_allowed_amount: int) -> HexBytes: + """ + ミンターを構成します。 + + Args: + minter: ミンターアドレス + minter_allowed_amount: ミンター許容量 + + Returns: + HexBytes: トランザクションハッシュ + """ + ... + + async def mint(self, to: Address, amount: Union[int, float]) -> HexBytes: + """ + トークンをミントします。 + + Args: + to: 受信者アドレス + amount: ミントするトークンの量 + + Returns: + HexBytes: トランザクションハッシュ + """ + ... + + async def transfer(self, to: Address, value: Union[int, float]) -> HexBytes: + """ + Transfer tokens to another address + + Args: + to: Recipient address + value: Amount to transfer + + Returns: + HexBytes: Transaction hash + """ + ... + + async def transfer_from(self, from_address: Address, to: Address, value: Union[int, float]) -> HexBytes: + """ + Transfer tokens from one address to another (requires approval) + + Args: + from_address: Sender address + to: Recipient address + value: Amount to transfer + + Returns: + HexBytes: Transaction hash + """ + ... + + async def approve(self, spender: Address, value: Union[int, float]) -> HexBytes: + """ + Approve a spender to transfer tokens + + Args: + spender: Spender address + value: Amount to approve + + Returns: + HexBytes: Transaction hash + """ + ... + + async def increase_allowance(self, spender: Address, increment: Union[int, float]) -> HexBytes: + """ + Increase the spending allowance for a spender + + Args: + spender: Spender address + increment: Amount to increase the allowance by + + Returns: + HexBytes: Transaction hash + """ + ... + + async def decrease_allowance(self, spender: Address, decrement: Union[int, float]) -> HexBytes: + """ + Decrease the spending allowance for a spender + + Args: + spender: Spender address + decrement: Amount to decrease the allowance by + + Returns: + HexBytes: Transaction hash + """ + ... + + async def permit(self, owner: Address, spender: Address, value: Union[int, float], + deadline: Uint256, v: Uint8, r: Bytes32, s: Bytes32) -> HexBytes: + """ + Set allowance using EIP-2612 permit (gasless approval) + + Args: + owner: Token owner address + spender: Spender address + value: Amount to approve + deadline: Permit expiration timestamp + v: Signature v parameter + r: Signature r parameter + s: Signature s parameter + + Returns: + HexBytes: Transaction hash + """ + ... + + async def transfer_with_authorization(self, from_address: Address, to: Address, value: Union[int, float], + valid_after: Uint256, valid_before: Uint256, nonce: Bytes32, + v: Uint8, r: Bytes32, s: Bytes32) -> HexBytes: + """ + Execute a signed transfer + + Args: + from_address: Sender address + to: Recipient address + value: Amount to transfer + valid_after: Starting validity timestamp + valid_before: Ending validity timestamp + nonce: Unique nonce + v: Signature v parameter + r: Signature r parameter + s: Signature s parameter + + Returns: + HexBytes: Transaction hash + """ + ... + + async def receive_with_authorization(self, from_address: Address, to: Address, value: Union[int, float], + valid_after: Uint256, valid_before: Uint256, nonce: Bytes32, + v: Uint8, r: Bytes32, s: Bytes32) -> HexBytes: + """ + Execute a signed receive + + Args: + from_address: Sender address + to: Recipient address + value: Amount to transfer + valid_after: Starting validity timestamp + valid_before: Ending validity timestamp + nonce: Unique nonce + v: Signature v parameter + r: Signature r parameter + s: Signature s parameter + + Returns: + HexBytes: Transaction hash + """ + ... + + async def cancel_authorization(self, authorizer: Address, nonce: Bytes32, + v: Uint8, r: Bytes32, s: Bytes32) -> HexBytes: + """ + Cancel a signed authorization + + Args: + authorizer: Authorizer address + nonce: Authorization nonce + v: Signature v parameter + r: Signature r parameter + s: Signature s parameter + + Returns: + HexBytes: Transaction hash + """ + ... \ No newline at end of file diff --git a/jpyc_sdk/jpyc.py b/jpyc_sdk/jpyc.py new file mode 100644 index 0000000..9ec2bf8 --- /dev/null +++ b/jpyc_sdk/jpyc.py @@ -0,0 +1,461 @@ +"""Core module for JPYC token operations""" + +import os +from typing import Optional, Union, Dict, Any, Callable, TypeVar, cast +import asyncio +from web3 import Web3 +from web3.contract import Contract +from web3.contract.contract import ContractFunction +from hexbytes import HexBytes +from eth_account.signers.local import LocalAccount +from typing_extensions import Self + +from .interfaces.jpyc import IJPYC +from .interfaces.abis import JPYC_V2_ABI +from .utils.types import Address, Bytes32, Uint256, Uint8 +from .utils.addresses import V2_PROXY_ADDRESS, LOCAL_PROXY_ADDRESS, is_valid_address +from .utils.errors import InvalidAddressError, InvalidTransactionError +from .utils.math import remove_decimals, restore_decimals + +T = TypeVar('T') # Define a type variable + +class JPYC(IJPYC): + """ + Client for interacting with the JPYC token contract. + """ + + def __init__(self, web3: Web3, account: Optional[LocalAccount] = None): + """ + Initialize the JPYC client. + + Args: + web3: Web3 instance + account: Account to use (optional) + + Raises: + InvalidAddressError: If the contract address is invalid + """ + self.web3 = web3 + self.account = account + + # Determine contract address based on environment + if os.environ.get('SDK_ENV') == 'local': + self.contract_address = LOCAL_PROXY_ADDRESS + else: + self.contract_address = V2_PROXY_ADDRESS + + if not is_valid_address(self.contract_address): + raise InvalidAddressError(self.contract_address) + + # Create contract instance + self.contract = self.web3.eth.contract( + address=self.contract_address, + abi=JPYC_V2_ABI + ) + + # Ensure default account is set + if self.account and not self.web3.eth.default_account: + self.web3.eth.default_account = self.account.address + + # Common async helper methods + async def _call_async(self, func: Callable[[], T]) -> T: + """ + Helper method to call synchronous functions asynchronously + + Args: + func: Function to call + + Returns: + The result of the function call + """ + return await asyncio.to_thread(func) + + async def _execute_transaction(self, contract_fn: ContractFunction) -> HexBytes: + """ + Common helper method for executing transactions + + Args: + contract_fn: Contract function to execute + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction fails + """ + if not self.account: + raise InvalidTransactionError(Exception("No account is set")) + + if not self.web3.eth.default_account: + self.web3.eth.default_account = self.account.address + + try: + account = cast(LocalAccount, self.account) + default_account = cast(Address, self.web3.eth.default_account) + + # Simulation (gas estimation) + gas_estimate = await self._call_async( + lambda: contract_fn.estimate_gas({'from': default_account}) + ) + + # Build transaction + nonce = await self._call_async( + lambda: self.web3.eth.get_transaction_count(default_account) + ) + + tx = await self._call_async( + lambda: contract_fn.build_transaction({ + 'from': default_account, + 'gas': gas_estimate, + 'nonce': nonce, + }) + ) + + # Sign and send transaction + signed_tx = await self._call_async( + lambda: self.web3.eth.account.sign_transaction(tx, private_key=account.key) + ) + + tx_hash = await self._call_async( + lambda: self.web3.eth.send_raw_transaction(signed_tx.rawTransaction) + ) + + return tx_hash + + except Exception as e: + # More detailed error handling + if "insufficient funds" in str(e).lower(): + raise InvalidTransactionError(Exception(f"Insufficient funds: {e}")) + elif "gas required exceeds allowance" in str(e).lower(): + raise InvalidTransactionError(Exception(f"Gas limit exceeded: {e}")) + else: + raise InvalidTransactionError(Exception(f"Transaction error: {e}")) + + # View functions (read-only) + async def is_minter(self, account: Address) -> bool: + """ + Returns whether the specified address is a minter. + + Args: + account: Account address + + Returns: + bool: True if minter, False otherwise + """ + return await self._call_async( + lambda: self.contract.functions.isMinter(account).call() + ) + + async def minter_allowance(self, minter: Address) -> float: + """ + Returns the minter's allowance (for minting). + + Args: + minter: Minter address + + Returns: + int: Minter allowance + """ + result = await self._call_async( + lambda: self.contract.functions.minterAllowance(minter).call() + ) + return restore_decimals(result) + + async def total_supply(self) -> float: + """ + Returns the total supply of JPYC tokens. + + Returns: + int: Total supply + """ + result = await self._call_async( + lambda: self.contract.functions.totalSupply().call() + ) + return restore_decimals(result) + + async def balance_of(self, account: Address) -> float: + """ + Returns the account balance. + + Args: + account: Account address + + Returns: + int: Account balance + """ + result = await self._call_async( + lambda: self.contract.functions.balanceOf(account).call() + ) + return restore_decimals(result) + + async def allowance(self, owner: Address, spender: Address) -> float: + """ + Returns the spender's allowance for the owner's tokens (for transfers). + + Args: + owner: Owner address + spender: Spender address + + Returns: + int: Spender allowance + """ + result = await self._call_async( + lambda: self.contract.functions.allowance(owner, spender).call() + ) + return restore_decimals(result) + + async def nonces(self, owner: Address) -> int: + """ + Returns the nonce for EIP2612 `permit`. + + Args: + owner: Owner address + + Returns: + int: Owner nonce + """ + return await self._call_async( + lambda: self.contract.functions.nonces(owner).call() + ) + + # State-changing functions (write) + async def configure_minter(self, minter: Address, minter_allowed_amount: int) -> HexBytes: + """ + Configure a minter. + + Args: + minter: Minter address + minter_allowed_amount: Minter allowance + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + amount_wei = remove_decimals(minter_allowed_amount) + return await self._execute_transaction( + self.contract.functions.configureMinter(minter, amount_wei) + ) + + async def mint(self, to: Address, amount: Union[int, float]) -> HexBytes: + """ + Mint tokens. + + Args: + to: Recipient address + amount: Amount of tokens to mint + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + amount_wei = remove_decimals(amount) + return await self._execute_transaction( + self.contract.functions.mint(to, amount_wei) + ) + + async def transfer(self, to: Address, value: Union[int, float]) -> HexBytes: + """ + Transfer tokens (directly). + + Args: + to: Recipient address + value: Amount of tokens to transfer + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + value_wei = remove_decimals(value) + return await self._execute_transaction( + self.contract.functions.transfer(to, value_wei) + ) + + async def transfer_from(self, from_address: Address, to: Address, value: Union[int, float]) -> HexBytes: + """ + Transfer tokens (from spender). + + Args: + from_address: Owner address + to: Recipient address + value: Amount of tokens to transfer + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + value_wei = remove_decimals(value) + return await self._execute_transaction( + self.contract.functions.transferFrom(from_address, to, value_wei) + ) + + async def approve(self, spender: Address, value: Union[int, float]) -> HexBytes: + """ + Set the spender's allowance for the owner's tokens. + + Args: + spender: Spender address + value: Allowance + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + value_wei = remove_decimals(value) + return await self._execute_transaction( + self.contract.functions.approve(spender, value_wei) + ) + + async def increase_allowance(self, spender: Address, increment: Union[int, float]) -> HexBytes: + """ + Increase allowance. + + Args: + spender: Spender address + increment: Amount to increase allowance by + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + increment_wei = remove_decimals(increment) + return await self._execute_transaction( + self.contract.functions.increaseAllowance(spender, increment_wei) + ) + + async def decrease_allowance(self, spender: Address, decrement: Union[int, float]) -> HexBytes: + """ + Decrease allowance. + + Args: + spender: Spender address + decrement: Amount to decrease allowance by + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + decrement_wei = remove_decimals(decrement) + return await self._execute_transaction( + self.contract.functions.decreaseAllowance(spender, decrement_wei) + ) + + async def permit(self, owner: Address, spender: Address, value: Union[int, float], + deadline: Uint256, v: Uint8, r: Bytes32, s: Bytes32) -> HexBytes: + """ + Set allowance using EIP2612 permit. + + Args: + owner: Owner address + spender: Spender address + value: Allowance + deadline: Deadline + v: v parameter of the signature + r: r parameter of the signature + s: s parameter of the signature + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + value_wei = remove_decimals(value) + return await self._execute_transaction( + self.contract.functions.permit(owner, spender, value_wei, deadline, v, r, s) + ) + + async def transfer_with_authorization(self, from_address: Address, to: Address, value: Union[int, float], + valid_after: Uint256, valid_before: Uint256, nonce: Bytes32, + v: Uint8, r: Bytes32, s: Bytes32) -> HexBytes: + """ + Execute a signed transfer. + + Args: + from_address: Sender address + to: Recipient address + value: Amount to transfer + valid_after: Start of validity period + valid_before: End of validity period + nonce: Nonce + v: v parameter of the signature + r: r parameter of the signature + s: s parameter of the signature + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + value_wei = remove_decimals(value) + return await self._execute_transaction( + self.contract.functions.transferWithAuthorization( + from_address, to, value_wei, valid_after, valid_before, nonce, v, r, s + ) + ) + + async def receive_with_authorization(self, from_address: Address, to: Address, value: Union[int, float], + valid_after: Uint256, valid_before: Uint256, nonce: Bytes32, + v: Uint8, r: Bytes32, s: Bytes32) -> HexBytes: + """ + Execute a signed receive. + + Args: + from_address: Sender address + to: Recipient address + value: Amount to transfer + valid_after: Start of validity period + valid_before: End of validity period + nonce: Nonce + v: v parameter of the signature + r: r parameter of the signature + s: s parameter of the signature + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + value_wei = remove_decimals(value) + return await self._execute_transaction( + self.contract.functions.receiveWithAuthorization( + from_address, to, value_wei, valid_after, valid_before, nonce, v, r, s + ) + ) + + async def cancel_authorization(self, authorizer: Address, nonce: Bytes32, + v: Uint8, r: Bytes32, s: Bytes32) -> HexBytes: + """ + Cancel a signed authorization. + + Args: + authorizer: Authorizer address + nonce: Nonce + v: v parameter of the signature + r: r parameter of the signature + s: s parameter of the signature + + Returns: + HexBytes: Transaction hash + + Raises: + InvalidTransactionError: If the transaction simulation fails + """ + return await self._execute_transaction( + self.contract.functions.cancelAuthorization(authorizer, nonce, v, r, s) + ) \ No newline at end of file diff --git a/jpyc_sdk/utils/__init__.py b/jpyc_sdk/utils/__init__.py new file mode 100644 index 0000000..d285945 --- /dev/null +++ b/jpyc_sdk/utils/__init__.py @@ -0,0 +1,5 @@ +from .addresses import * +from .chains import * +from .errors import * +from .math import * +from .types import * \ No newline at end of file diff --git a/jpyc_sdk/utils/addresses.py b/jpyc_sdk/utils/addresses.py new file mode 100644 index 0000000..fe217fc --- /dev/null +++ b/jpyc_sdk/utils/addresses.py @@ -0,0 +1,32 @@ +"""Address definitions and utility module + +This module provides constant definitions for important contract addresses used in the JPYC SDK, +as well as utility functions for address validation. +It includes basic address constants such as proxy contract addresses and the zero address. +""" + +import os +from web3 import Web3 + +from .types import Address + +# Address definitions +ZERO_ADDRESS = Address(Web3.to_checksum_address('0x0000000000000000000000000000000000000000')) +V2_PROXY_ADDRESS = Address(Web3.to_checksum_address('0x431D5dfF03120AFA4bDf332c61A6e1766eF37BDB')) +LOCAL_PROXY_ADDRESS = Address(Web3.to_checksum_address(os.environ.get('LOCAL_PROXY_ADDRESS', '0x0000000000000000000000000000000000000000'))) + +def is_valid_address(address: str) -> bool: + """ + Checks if the specified address is a valid Ethereum address. + + Args: + address: Address string to check + + Returns: + bool: True if valid, False otherwise + """ + try: + Web3.to_checksum_address(address) + return True + except ValueError: + return False \ No newline at end of file diff --git a/jpyc_sdk/utils/chains.py b/jpyc_sdk/utils/chains.py new file mode 100644 index 0000000..06c573e --- /dev/null +++ b/jpyc_sdk/utils/chains.py @@ -0,0 +1,219 @@ +"""Chain and network configuration module + +This module provides definitions of blockchains and networks supported by the JPYC SDK, +as well as utility functions for working with them. +It includes functionality for validating supported chains and networks, and configuring Web3 instances. +""" + +from typing import Dict, Any, List +from web3 import Web3 +from web3.middleware import ExtraDataToPOAMiddleware + +from .types import ChainName, NetworkName + +# Chain and network definitions +SUPPORTED_CHAINS = { + "local": { + "mainnet": { + "id": 31337, + "name": "Hardhat", + "native_currency": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpc_urls": ["http://127.0.0.1:8545/"] + } + }, + "ethereum": { + "mainnet": { + "id": 1, + "name": "Ethereum Mainnet", + "native_currency": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpc_urls": ["https://ethereum.publicnode.com"] + }, + "goerli": { + "id": 5, + "name": "Goerli Testnet", + "native_currency": { + "name": "Goerli Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpc_urls": ["https://goerli.infura.io/v3/"] + }, + "sepolia": { + "id": 11155111, + "name": "Sepolia Testnet", + "native_currency": { + "name": "Sepolia Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpc_urls": ["https://sepolia.infura.io/v3/"] + } + }, + "polygon": { + "mainnet": { + "id": 137, + "name": "Polygon Mainnet", + "native_currency": { + "name": "MATIC", + "symbol": "MATIC", + "decimals": 18 + }, + "rpc_urls": ["https://polygon-rpc.com"] + }, + "amoy": { + "id": 80002, + "name": "Polygon Amoy Testnet", + "native_currency": { + "name": "MATIC", + "symbol": "MATIC", + "decimals": 18 + }, + "rpc_urls": ["https://rpc-amoy.polygon.technology"] + } + }, + "gnosis": { + "mainnet": { + "id": 100, + "name": "Gnosis Chain", + "native_currency": { + "name": "xDAI", + "symbol": "xDAI", + "decimals": 18 + }, + "rpc_urls": ["https://rpc.gnosischain.com"] + }, + "chiado": { + "id": 10200, + "name": "Chiado Testnet", + "native_currency": { + "name": "xDAI", + "symbol": "xDAI", + "decimals": 18 + }, + "rpc_urls": ["https://rpc.chiadochain.net"] + } + }, + "avalanche": { + "mainnet": { + "id": 43114, + "name": "Avalanche C-Chain", + "native_currency": { + "name": "Avalanche", + "symbol": "AVAX", + "decimals": 18 + }, + "rpc_urls": ["https://api.avax.network/ext/bc/C/rpc"] + }, + "fuji": { + "id": 43113, + "name": "Avalanche Fuji Testnet", + "native_currency": { + "name": "Avalanche", + "symbol": "AVAX", + "decimals": 18 + }, + "rpc_urls": ["https://api.avax-test.network/ext/bc/C/rpc"] + } + }, + "astar": { + "mainnet": { + "id": 592, + "name": "Astar Network", + "native_currency": { + "name": "Astar", + "symbol": "ASTR", + "decimals": 18 + }, + "rpc_urls": ["https://astar.public.blastapi.io"] + } + }, + "shiden": { + "mainnet": { + "id": 336, + "name": "Shiden Network", + "native_currency": { + "name": "Shiden", + "symbol": "SDN", + "decimals": 18 + }, + "rpc_urls": ["https://shiden.public.blastapi.io"] + } + } +} + +def get_supported_chain_names() -> List[str]: + """ + Get a list of supported chain names. + + Returns: + List[str]: List of supported chain names + """ + return list(SUPPORTED_CHAINS.keys()) + +def is_valid_chain_name(chain_name: str) -> bool: + """ + Check if the specified chain name is supported. + + Args: + chain_name: Chain name to check + + Returns: + bool: True if supported, False otherwise + """ + return chain_name in SUPPORTED_CHAINS + +def get_supported_network_names(chain_name: str) -> List[str]: + """ + Get a list of supported network names for the specified chain. + + Args: + chain_name: Chain name + + Returns: + List[str]: List of supported network names + """ + if chain_name in SUPPORTED_CHAINS: + return list(SUPPORTED_CHAINS[chain_name].keys()) + return [] + +def is_valid_network_name(chain_name: str, network_name: str) -> bool: + """ + Check if the specified network name is supported for the specified chain. + + Args: + chain_name: Chain name + network_name: Network name + + Returns: + bool: True if supported, False otherwise + """ + return chain_name in SUPPORTED_CHAINS and network_name in SUPPORTED_CHAINS[chain_name] + +def get_web3_for_chain(chain_name: ChainName, network_name: NetworkName, rpc_endpoint: str) -> Web3: + """ + Get a Web3 instance configured for the specified chain and network. + + Args: + chain_name: Chain name + network_name: Network name + rpc_endpoint: RPC endpoint URL + + Returns: + Web3: Configured Web3 instance + """ + web3 = Web3(Web3.HTTPProvider(rpc_endpoint)) + + # Add POA middleware for chains that require it + poa_chains = ["goerli", "sepolia", "polygon", "gnosis"] + if chain_name in poa_chains or (chain_name == "ethereum" and network_name in ["goerli", "sepolia"]): + web3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) + + return web3 \ No newline at end of file diff --git a/jpyc_sdk/utils/errors.py b/jpyc_sdk/utils/errors.py new file mode 100644 index 0000000..b8e8577 --- /dev/null +++ b/jpyc_sdk/utils/errors.py @@ -0,0 +1,38 @@ +"""Error definition module + +This module defines exception classes used by the JPYC SDK. +All exceptions inherit from the base class `JpycSdkError` to clearly identify SDK-specific errors. +Each exception class corresponds to a specific error situation and provides appropriate error messages. +""" + +from .chains import get_supported_chain_names, get_supported_network_names + +class JpycSdkError(Exception): + """Base class for all JPYC SDK errors""" + pass + +class InvalidChainNameError(JpycSdkError): + """Raised when an unsupported blockchain name is specified""" + def __init__(self, chain_name: str): + supported_chains = ', '.join(get_supported_chain_names()) + super().__init__( + f"Invalid chain '{chain_name}' (supported chain names = {{{supported_chains}}})." + ) + +class InvalidNetworkNameError(JpycSdkError): + """Raised when an unsupported network name is specified for a given chain""" + def __init__(self, chain_name: str, network_name: str): + supported_networks = ', '.join(get_supported_network_names(chain_name)) + super().__init__( + f"Invalid network '{network_name}' (supported network names = {{{supported_networks}}})." + ) + +class InvalidAddressError(JpycSdkError): + """Raised when an invalid Ethereum address is provided""" + def __init__(self, address: str): + super().__init__(f"Invalid address '{address}'.") + +class InvalidTransactionError(JpycSdkError): + """Raised when a transaction simulation fails""" + def __init__(self, error: Exception): + super().__init__(f"Transaction simulation failed.\n{str(error)}") \ No newline at end of file diff --git a/jpyc_sdk/utils/math.py b/jpyc_sdk/utils/math.py new file mode 100644 index 0000000..69e4428 --- /dev/null +++ b/jpyc_sdk/utils/math.py @@ -0,0 +1,77 @@ +"""Mathematical utility module + +This module provides utility functions related to token numerical calculations. +It includes functionality for handling 18 decimal places commonly used in Ethereum-based tokens, +conversions to uint256 type, and other numerical representations used in blockchain. +""" + +from decimal import Decimal +from typing import Union + +from .types import Uint256 + +# Number of decimal places for the token +TOKEN_DECIMALS = 18 +DECIMALS_QUANTIFIER = 10 ** TOKEN_DECIMALS + +def to_uint256(value: Union[int, str]) -> Uint256: + """ + Convert an integer value or its string representation to a uint256 representation. + + Args: + value: Integer value or string representation + + Returns: + Uint256: Value represented as uint256 + + Raises: + TypeError: If value is not an integer or string + ValueError: If value is outside the range of uint256 + """ + if isinstance(value, str): + return Uint256.from_string(value) + elif isinstance(value, int): + return Uint256(value) + else: + raise TypeError(f"Expected int or str, got {type(value).__name__}") + +def remove_decimals(value: Union[int, float, Decimal]) -> Uint256: + """ + Removes the decimal point so that only integer values exist on-chain. + Example: + 0.01 -> 10,000,000,000,000,000 + 100 -> 100,000,000,000,000,000,000 + + Args: + value: Integer or decimal value + + Returns: + Uint256: Value represented as uint256 + + Raises: + ValueError: If the calculated result is outside the range of uint256 + """ + if isinstance(value, Decimal): + decimal_value = int(value * Decimal(DECIMALS_QUANTIFIER)) + return Uint256(decimal_value) + else: + float_value = int(value * DECIMALS_QUANTIFIER) + return Uint256(float_value) + +def restore_decimals(value: Union[Uint256, int]) -> float: + """ + Restores decimal places, mainly for display purposes. + Example: + 10,000,000,000,000,000 -> 0.01 + 100,000,000,000,000,000,000 -> 100 + + Args: + value: uint256 value or integer value + + Returns: + float: Value represented as a number (integer or decimal) + """ + if isinstance(value, Uint256): + return float(value.to_int()) / float(DECIMALS_QUANTIFIER) + else: + return float(value) / float(DECIMALS_QUANTIFIER) \ No newline at end of file diff --git a/jpyc_sdk/utils/types.py b/jpyc_sdk/utils/types.py new file mode 100644 index 0000000..57faed0 --- /dev/null +++ b/jpyc_sdk/utils/types.py @@ -0,0 +1,162 @@ +"""Type definition module + +This module provides type definitions used in the JPYC SDK. +It defines basic types such as Ethereum addresses, numeric types, and names of chains and environments. +These type definitions are used throughout the SDK to ensure type safety and improve developer experience. +""" + +from typing import Literal, TypeAlias, NewType, Union, Any +from typing_extensions import Annotated +from web3.types import ChecksumAddress + +# Definition of address and related types +Address = NewType('Address', ChecksumAddress) +Bytes32 = NewType('Bytes32', str) +Endpoint: TypeAlias = str + +# Definition of supported chains and networks +ChainName = Literal['local', 'ethereum', 'polygon', 'gnosis', 'avalanche', 'astar', 'shiden'] +NetworkName = Literal['mainnet', 'goerli', 'sepolia', 'amoy', 'chiado', 'fuji'] + +class Uint8: + """ + A lightweight class representing Solidity's uint8 type. + + - Only holds a value in the range [0, 255]. + - Stores the internal value as an integer. + - Provides range checks and simple int conversion. + + Examples: + >>> u = Uint8(123) + >>> u.to_int() + 123 + >>> u == 123 + True + """ + + __slots__ = ("_value",) + + def __init__(self, value: int): + """ + Initialize a Uint8 instance from a Python int. + + Args: + value: Integer value between 0 and 255 + + Raises: + TypeError: If value is not an integer + ValueError: If value is outside the range of uint8 + """ + if not isinstance(value, int): + raise TypeError(f"Expected int, got {type(value).__name__}") + if value < 0 or value > 255: + raise ValueError(f"Value {value} is outside the range of uint8 (0..255)") + self._value = value + + def to_int(self) -> int: + """Returns the stored integer value.""" + return self._value + + def __eq__(self, other: object) -> bool: + """ + Equality comparison with either another Uint8 or an int. + """ + if isinstance(other, Uint8): + return self._value == other._value + elif isinstance(other, int): + return self._value == other + return NotImplemented + + def __repr__(self) -> str: + """Debug representation.""" + return f"Uint8({self._value})" + +class Uint256: + """ + A lightweight class representing Solidity's uint256 type without operator overloading. + + - Only holds a value in the range [0, 2^256 - 1]. + - Stores the internal value as an integer (Python int is arbitrary-precision). + - Provides range checks and simple string/int conversion. + + Examples: + >>> u = Uint256.from_value(123) + >>> u.to_string() + '123' + >>> u.to_int() + 123 + """ + + def __init__(self, value: int): + """ + Initialize a Uint256 instance from a Python int. + + Args: + value: Integer value between 0 and 2^256 - 1 + + Raises: + TypeError: If value is not an integer + ValueError: If value is outside the range of uint256 + """ + if not isinstance(value, int): + raise TypeError(f"Expected int, got {type(value).__name__}") + if value < 0 or value >= 2**256: + raise ValueError(f"Value {value} is outside the range of uint256 (0 to 2^256-1)") + + self._value = value + + def to_int(self) -> int: + """Returns the stored integer value.""" + return self._value + + def to_string(self) -> str: + """Returns the decimal string representation of this uint256.""" + return str(self._value) + + def __str__(self) -> str: + """Same as `to_string()`.""" + return str(self._value) + + def __repr__(self) -> str: + """Debug representation.""" + return f"Uint256({self._value})" + + def __eq__(self, other: Any) -> bool: + """ + Equality comparison with either another Uint256 or an int. + """ + if isinstance(other, Uint256): + return self._value == other._value + elif isinstance(other, int): + return self._value == other + return NotImplemented + + @classmethod + def from_string(cls, s: str) -> 'Uint256': + """ + Create a Uint256 from a decimal string. + + Raises: + ValueError: if the string is not a valid integer or out of range + """ + try: + as_int = int(s, 10) # decimal parse + except ValueError: + raise ValueError(f"Cannot convert '{s}' to Uint256") + return cls(as_int) + + @classmethod + def from_value(cls, val: Union[int, str]) -> 'Uint256': + """ + Factory method. Accepts either an int or decimal string, similar to TS's Uint256.from(). + + Examples: + Uint256.from_value(123) + Uint256.from_value("123") + """ + if isinstance(val, int): + return cls(val) + elif isinstance(val, str): + return cls.from_string(val) + else: + raise TypeError(f"Expected int or str, got {type(val).__name__}") \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0491f89 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,65 @@ +[tool.poetry] +name = "jpyc-sdk-python" +version = "1.0.0" +description = "Python SDK for JPYC - Japanese Yen Stablecoin" +authors = ["mameta "] +license = "MIT" +readme = "README.md" +repository = "https://github.com/yourusername/jpyc-sdk-python" +keywords = ["jpyc", "stablecoin", "ethereum", "web3", "blockchain"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", +] +packages = [{include = "jpyc_sdk"}] + +[tool.poetry.dependencies] +python = ">=3.11,<4.0" +web3 = ">=7.9.0,<8.0.0" +pydantic = ">=2.10.6,<3.0.0" +typing-extensions = ">=4.12.2,<5.0.0" +python-dotenv = ">=1.0.1,<2.0.0" +hexbytes = ">=1.2.0" # 修正: web3の要件に合わせてアップデート + +[tool.poetry.group.dev.dependencies] +pytest = "^8.3.5" +mypy = "^1.15.0" +black = "^25.1.0" +sphinx = "^7.2.6" +pytest-asyncio = "^0.25.3" +pytest-cov = "^6.0.0" +sphinx-rtd-theme = "^2.0.0" +pytest-mock = "^3.14.0" +myst-parser = "^3.0.0" + +# ドキュメント生成用のスクリプトを修正 +[tool.poetry.scripts] +# 基本的なドキュメント生成コマンド +docs = "sphinx.cmd.build:main" +docs-clean = "sphinx.cmd.build:main" + +# APIドキュメント生成コマンド(簡素化) +sphinx-apidoc = "sphinx.ext.apidoc:main" +sphinx-build = "sphinx.cmd.build:main" + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.black] +line-length = 100 +target-version = ["py311"] + +[tool.mypy] +python_version = "3.11" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true +disallow_incomplete_defs = true + +[tool.pytest.ini_options] +testpaths = ["tests"] +asyncio_mode = "auto" \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..b28b04f --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,3 @@ + + + diff --git a/tests/test_client.py b/tests/test_client.py new file mode 100644 index 0000000..0f93ec9 --- /dev/null +++ b/tests/test_client.py @@ -0,0 +1,170 @@ +""" +Tests for SdkClient implementation +""" + +import os +import pytest +from unittest.mock import patch, MagicMock +from web3 import Web3 +from web3.providers.eth_tester import EthereumTesterProvider +from eth_account import Account + +from jpyc_sdk.client import SdkClient +from jpyc_sdk.utils.errors import InvalidChainNameError, InvalidNetworkNameError + +# Test fixtures +@pytest.fixture +def eth_tester_provider(): + return EthereumTesterProvider() + +@pytest.fixture +def web3(eth_tester_provider): + return Web3(eth_tester_provider) + +@pytest.fixture +def valid_chain_name(): + return "ethereum" + +@pytest.fixture +def valid_network_name(): + return "mainnet" + +@pytest.fixture +def valid_rpc_endpoint(): + return "https://ethereum.publicnode.com" + +@pytest.fixture +def sdk_client(valid_chain_name, valid_network_name, valid_rpc_endpoint): + with patch('jpyc_sdk.client.get_web3_for_chain') as mock_get_web3: + mock_web3 = MagicMock() + mock_get_web3.return_value = mock_web3 + client = SdkClient(valid_chain_name, valid_network_name, valid_rpc_endpoint) + return client + +# Tests for SdkClient.__init__ +def test_init_with_valid_inputs(valid_chain_name, valid_network_name, valid_rpc_endpoint): + """Test initialization with valid inputs""" + with patch('jpyc_sdk.client.get_web3_for_chain') as mock_get_web3: + mock_web3 = MagicMock() + mock_get_web3.return_value = mock_web3 + + client = SdkClient(valid_chain_name, valid_network_name, valid_rpc_endpoint) + + assert client.chain_name == valid_chain_name + assert client.network_name == valid_network_name + assert client.rpc_endpoint == valid_rpc_endpoint + assert client.web3 == mock_web3 + assert client.account is None + +def test_init_with_invalid_chain_name(valid_network_name, valid_rpc_endpoint): + """Test initialization with invalid chain name""" + with pytest.raises(InvalidChainNameError): + SdkClient("invalid_chain", valid_network_name, valid_rpc_endpoint) + +def test_init_with_invalid_network_name(valid_chain_name, valid_rpc_endpoint): + """Test initialization with invalid network name""" + with pytest.raises(InvalidNetworkNameError): + SdkClient(valid_chain_name, "invalid_network", valid_rpc_endpoint) + +# Tests for SdkClient.create_private_key_account +def test_create_private_key_account_with_explicit_private_key(sdk_client): + """Test creating an account with an explicitly provided private key""" + private_key = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + + with patch('jpyc_sdk.client.Account.from_key') as mock_from_key: + mock_account = MagicMock() + mock_from_key.return_value = mock_account + + account = sdk_client.create_private_key_account(private_key) + + mock_from_key.assert_called_once_with(private_key) + assert account == mock_account + assert sdk_client.account == mock_account + +def test_create_private_key_account_with_env_variable(sdk_client): + """Test creating an account with a private key from environment variable""" + private_key = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + + with patch.dict(os.environ, {"PRIVATE_KEY": private_key}): + with patch('jpyc_sdk.client.Account.from_key') as mock_from_key: + mock_account = MagicMock() + mock_from_key.return_value = mock_account + + account = sdk_client.create_private_key_account() + + mock_from_key.assert_called_once_with(private_key) + assert account == mock_account + assert sdk_client.account == mock_account + +def test_create_private_key_account_with_no_private_key(sdk_client): + """Test creating an account with no private key provided""" + with patch.dict(os.environ, {}, clear=True): # Clear environment variables + with pytest.raises(ValueError) as excinfo: + sdk_client.create_private_key_account() + + assert "Private key not provided" in str(excinfo.value) + +# Tests for SdkClient.create_local_client +def test_create_local_client_with_explicit_account(sdk_client): + """Test creating a local client with an explicitly provided account""" + mock_account = MagicMock() + mock_account.address = "0x1234567890123456789012345678901234567890" + + result = sdk_client.create_local_client(mock_account) + + # Check middleware was added + sdk_client.web3.middleware_onion.inject.assert_called_once() + assert sdk_client.web3.eth.default_account == mock_account.address + assert result == sdk_client.web3 + +def test_create_local_client_with_previously_created_account(sdk_client): + """Test creating a local client with a previously created account""" + mock_account = MagicMock() + mock_account.address = "0x1234567890123456789012345678901234567890" + sdk_client.account = mock_account + + result = sdk_client.create_local_client() + + # Check middleware was added + sdk_client.web3.middleware_onion.inject.assert_called_once() + assert sdk_client.web3.eth.default_account == mock_account.address + assert result == sdk_client.web3 + +def test_create_local_client_with_address_string(sdk_client): + """Test creating a local client with an address string""" + address = "0x1234567890123456789012345678901234567890" + + with patch('jpyc_sdk.client.Web3.to_checksum_address') as mock_to_checksum: + mock_to_checksum.return_value = address + + result = sdk_client.create_local_client(address) + + mock_to_checksum.assert_called_once_with(address) + assert sdk_client.web3.eth.default_account == address + assert result == sdk_client.web3 + +def test_create_local_client_with_no_account(sdk_client): + """Test creating a local client with no account provided""" + with pytest.raises(ValueError) as excinfo: + sdk_client.create_local_client() + + assert "No account provided" in str(excinfo.value) + +def test_create_local_client_with_missing_web3(sdk_client): + """Test creating a local client when web3 instance is missing""" + sdk_client.web3 = None + mock_account = MagicMock() + mock_account.address = "0x1234567890123456789012345678901234567890" + sdk_client.account = mock_account + + with patch('jpyc_sdk.client.Web3') as mock_web3_class: + mock_web3 = MagicMock() + mock_web3_class.HTTPProvider.return_value = MagicMock() + mock_web3_class.return_value = mock_web3 + + result = sdk_client.create_local_client() + + mock_web3_class.HTTPProvider.assert_called_once_with(sdk_client.rpc_endpoint) + mock_web3_class.assert_called_once() + assert sdk_client.web3 == mock_web3 + assert result == mock_web3 \ No newline at end of file diff --git a/tests/test_jpyc.py b/tests/test_jpyc.py new file mode 100644 index 0000000..1cd2be9 --- /dev/null +++ b/tests/test_jpyc.py @@ -0,0 +1,288 @@ +import pytest +import os +from unittest.mock import patch, MagicMock, AsyncMock +from web3 import Web3 +from eth_account.signers.local import LocalAccount +from hexbytes import HexBytes +import asyncio + +from jpyc_sdk.jpyc import JPYC +from jpyc_sdk.utils.errors import InvalidAddressError, InvalidTransactionError +from jpyc_sdk.utils.math import DECIMALS_QUANTIFIER +from jpyc_sdk.utils.addresses import LOCAL_PROXY_ADDRESS + + +@pytest.fixture +def mock_web3(): + """Fixture for creating a Web3 mock""" + web3 = MagicMock() + # Mock eth property properly + eth_mock = MagicMock() + contract_mock = MagicMock() + eth_mock.contract.return_value = contract_mock + eth_mock.default_account = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' + + # Set required properties and methods for Web3 mock + web3.eth = eth_mock + web3.to_checksum_address.side_effect = lambda x: x + + return web3 + + +@pytest.fixture +def mock_account(): + """Fixture for creating an account mock""" + account = MagicMock(spec=LocalAccount) + account.address = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' + account.key = b'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' + return account + + +@pytest.fixture +def jpyc_instance(mock_web3, mock_account): + """Fixture for creating a JPYC class instance""" + os.environ['SDK_ENV'] = 'production' + + # Apply patch + with patch('jpyc_sdk.jpyc.is_valid_address', return_value=True): + instance = JPYC(mock_web3, mock_account) + return instance + + +class TestJPYC: + def test_init_with_valid_address(self, mock_web3, mock_account): + """Test JPYC initialization with a valid address""" + # Set environment variable + os.environ['SDK_ENV'] = 'production' + + # Apply patch + with patch('jpyc_sdk.jpyc.is_valid_address', return_value=True): + jpyc = JPYC(mock_web3, mock_account) + + # Verification + assert jpyc.web3 == mock_web3 + assert jpyc.account == mock_account + mock_web3.eth.contract.assert_called_once() + + def test_init_with_invalid_address(self, mock_web3, mock_account): + """Test JPYC initialization with an invalid address""" + # Set environment variable + os.environ['SDK_ENV'] = 'production' + + # Apply patch + with patch('jpyc_sdk.jpyc.is_valid_address', return_value=False): + with pytest.raises(InvalidAddressError): + JPYC(mock_web3, mock_account) + + def test_init_with_local_env(self, mock_web3, mock_account): + """Test JPYC initialization with local environment""" + # Set environment variable + os.environ['SDK_ENV'] = 'local' + + # Apply patch + with patch('jpyc_sdk.jpyc.is_valid_address', return_value=True): + jpyc = JPYC(mock_web3, mock_account) + + # Verification + assert jpyc.contract_address == LOCAL_PROXY_ADDRESS + + @pytest.mark.asyncio + async def test_is_minter(self, jpyc_instance): + """Test is_minter method""" + # Use AsyncMock to mock the asynchronous method + jpyc_instance.contract.functions.isMinter.return_value.call = AsyncMock(return_value=True) + + # Use patch to replace the asynchronous method + with patch.object(JPYC, 'is_minter', new_callable=AsyncMock, return_value=True): + # Execute the asynchronous method with await + result = await jpyc_instance.is_minter('0x123') + assert result is True + + @pytest.mark.asyncio + async def test_minter_allowance(self, jpyc_instance): + """Test minter_allowance method""" + # Mock the contract call with wei value + jpyc_instance.contract.functions.minterAllowance.return_value.call = AsyncMock(return_value=5000 * DECIMALS_QUANTIFIER) + + # Use patch to replace the asynchronous method + with patch.object(JPYC, 'minter_allowance', new_callable=AsyncMock, return_value=5000.0): + # Execute the asynchronous method with await + result = await jpyc_instance.minter_allowance('0x123') + assert result == 5000.0 + + @pytest.mark.asyncio + async def test_total_supply(self, jpyc_instance): + """Test total_supply method""" + # Use AsyncMock to mock the asynchronous method + jpyc_instance.contract.functions.totalSupply.return_value.call = AsyncMock(return_value=1000000000000000000) + + # Use patch to replace the asynchronous method + with patch.object(JPYC, 'total_supply', new_callable=AsyncMock, return_value=1.0): + # Execute the asynchronous method with await + result = await jpyc_instance.total_supply() + assert result == 1.0 + + @pytest.mark.asyncio + async def test_balance_of(self, jpyc_instance): + """Test balance_of method""" + # Use AsyncMock to mock the asynchronous method + jpyc_instance.contract.functions.balanceOf.return_value.call = AsyncMock(return_value=500000000000000000) + + # Use patch to replace the asynchronous method + with patch.object(JPYC, 'balance_of', new_callable=AsyncMock, return_value=0.5): + # Execute the asynchronous method with await + result = await jpyc_instance.balance_of('0x123') + assert result == 0.5 + + @pytest.mark.asyncio + async def test_allowance(self, jpyc_instance): + """Test allowance method""" + # Mock the contract call + jpyc_instance.contract.functions.allowance.return_value.call = AsyncMock(return_value=2000 * DECIMALS_QUANTIFIER) + + # Use patch + with patch.object(JPYC, 'allowance', new_callable=AsyncMock, return_value=2000.0): + result = await jpyc_instance.allowance('0x123', '0x456') + assert result == 2000.0 + + @pytest.mark.asyncio + async def test_nonces(self, jpyc_instance): + """Test nonces method""" + # Mock the contract call + jpyc_instance.contract.functions.nonces.return_value.call = AsyncMock(return_value=5) + + # Use patch + with patch.object(JPYC, 'nonces', new_callable=AsyncMock, return_value=5): + result = await jpyc_instance.nonces('0x123') + assert result == 5 + + @pytest.mark.asyncio + async def test_execute_transaction_without_account(self, jpyc_instance): + """Test _execute_transaction method without account""" + # Remove account + jpyc_instance.account = None + + # Test function call + contract_fn = MagicMock() + + with pytest.raises(InvalidTransactionError) as excinfo: + await jpyc_instance._execute_transaction(contract_fn) + + assert "No account is set" in str(excinfo.value) + + @pytest.mark.asyncio + async def test_execute_transaction_with_insufficient_funds(self, jpyc_instance): + """Test _execute_transaction method with insufficient funds error""" + # Mock function that raises exception + contract_fn = MagicMock() + contract_fn.estimate_gas.side_effect = Exception("insufficient funds for gas") + + with pytest.raises(InvalidTransactionError) as excinfo: + await jpyc_instance._execute_transaction(contract_fn) + + assert "Insufficient funds" in str(excinfo.value) + + @pytest.mark.asyncio + async def test_execute_transaction_success(self, jpyc_instance): + """Test _execute_transaction method success case""" + # Mock the necessary functions + contract_fn = MagicMock() + contract_fn.estimate_gas.return_value = 100000 + contract_fn.build_transaction.return_value = {"gas": 100000, "nonce": 1} + + tx_hash = HexBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') + + # Mock web3 methods + jpyc_instance.web3.eth.get_transaction_count.return_value = 1 + jpyc_instance.web3.eth.account.sign_transaction.return_value.rawTransaction = b'signed_tx' + jpyc_instance.web3.eth.send_raw_transaction.return_value = tx_hash + + # Execute the function + result = await jpyc_instance._execute_transaction(contract_fn) + + # Verify + assert result == tx_hash + contract_fn.estimate_gas.assert_called_once() + contract_fn.build_transaction.assert_called_once() + jpyc_instance.web3.eth.account.sign_transaction.assert_called_once() + jpyc_instance.web3.eth.send_raw_transaction.assert_called_once_with(b'signed_tx') + + @pytest.mark.asyncio + async def test_transfer_success(self, jpyc_instance): + """Test transfer method success case""" + # Preset function patch + tx_hash = HexBytes('0x456') + + # Use patch to replace the asynchronous method + with patch.object(JPYC, 'transfer', new_callable=AsyncMock, return_value=tx_hash): + # Execute the asynchronous method with await + result = await jpyc_instance.transfer('0x123', 10.0) + assert result == tx_hash + + @pytest.mark.asyncio + async def test_transfer_failure(self, jpyc_instance): + """Test transfer method failure case""" + # Mock to throw an exception + with patch.object(JPYC, 'transfer', new_callable=AsyncMock, + side_effect=InvalidTransactionError("Transaction simulation failed: Transaction failed")): + # Verify that an exception occurs + with pytest.raises(InvalidTransactionError) as excinfo: + await jpyc_instance.transfer('0x123', 10.0) + + # Verify error message + assert "Transaction simulation failed" in str(excinfo.value) + assert "Transaction failed" in str(excinfo.value) + + @pytest.mark.asyncio + async def test_mint(self, jpyc_instance): + """Test mint method""" + tx_hash = HexBytes('0x789') + + with patch.object(JPYC, 'mint', new_callable=AsyncMock, return_value=tx_hash): + result = await jpyc_instance.mint('0x123', 50.0) + assert result == tx_hash + + @pytest.mark.asyncio + async def test_configure_minter(self, jpyc_instance): + """Test configure_minter method""" + tx_hash = HexBytes('0xabc') + + with patch.object(JPYC, 'configure_minter', new_callable=AsyncMock, return_value=tx_hash): + result = await jpyc_instance.configure_minter('0x123', 1000) + assert result == tx_hash + + @pytest.mark.asyncio + async def test_transfer_from(self, jpyc_instance): + """Test transfer_from method""" + tx_hash = HexBytes('0xdef') + + with patch.object(JPYC, 'transfer_from', new_callable=AsyncMock, return_value=tx_hash): + result = await jpyc_instance.transfer_from('0x123', '0x456', 25.0) + assert result == tx_hash + + @pytest.mark.asyncio + async def test_approve(self, jpyc_instance): + """Test approve method""" + tx_hash = HexBytes('0xaaa') + + with patch.object(JPYC, 'approve', new_callable=AsyncMock, return_value=tx_hash): + result = await jpyc_instance.approve('0x123', 500.0) + assert result == tx_hash + + @pytest.mark.asyncio + async def test_increase_allowance(self, jpyc_instance): + """Test increase_allowance method""" + tx_hash = HexBytes('0xbbb') + + with patch.object(JPYC, 'increase_allowance', new_callable=AsyncMock, return_value=tx_hash): + result = await jpyc_instance.increase_allowance('0x123', 100.0) + assert result == tx_hash + + @pytest.mark.asyncio + async def test_decrease_allowance(self, jpyc_instance): + """Test decrease_allowance method""" + tx_hash = HexBytes('0xccc') + + with patch.object(JPYC, 'decrease_allowance', new_callable=AsyncMock, return_value=tx_hash): + result = await jpyc_instance.decrease_allowance('0x123', 50.0) + assert result == tx_hash diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/tests/utils/__init__.py @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/utils/test_math.py b/tests/utils/test_math.py new file mode 100644 index 0000000..377d667 --- /dev/null +++ b/tests/utils/test_math.py @@ -0,0 +1,87 @@ +import pytest +from decimal import Decimal +from jpyc_sdk.utils.math import to_uint256, remove_decimals, restore_decimals +from jpyc_sdk.utils.types import Uint256 + +class TestMathFunctions: + def test_to_uint256(self): + # Convert integer to Uint256 + value = to_uint256(100) + assert isinstance(value, Uint256) + assert value.to_int() == 100 + + # Convert string to Uint256 + value = to_uint256("100") + assert isinstance(value, Uint256) + assert value.to_int() == 100 + + # Error for invalid values + with pytest.raises(ValueError): + to_uint256(-1) + + with pytest.raises(ValueError): + to_uint256(2**256) + + # Error for invalid type + with pytest.raises(TypeError): + to_uint256(1.5) + + def test_remove_decimals(self): + # Test with integer value + value = remove_decimals(1) + assert isinstance(value, Uint256) + assert value.to_int() == 10**18 + + # Test with decimal value + value = remove_decimals(0.01) + assert isinstance(value, Uint256) + assert value.to_int() == 10**16 + + # Test with Decimal type + value = remove_decimals(Decimal('0.01')) + assert isinstance(value, Uint256) + assert value.to_int() == 10**16 + + # Test with large value + value = remove_decimals(100) + assert isinstance(value, Uint256) + assert value.to_int() == 100 * 10**18 + + def test_restore_decimals(self): + # Convert from Uint256 + uint_value = Uint256(10**18) + value = restore_decimals(uint_value) + assert value == 1.0 + + # Convert from integer + value = restore_decimals(10**18) + assert value == 1.0 + + # Test with small value + uint_value = Uint256(10**16) + value = restore_decimals(uint_value) + assert value == 0.01 + + # Test with large value + uint_value = Uint256(100 * 10**18) + value = restore_decimals(uint_value) + assert value == 100.0 + + def test_round_trip(self): + # Convert integer value and restore + original = 100 + uint_value = remove_decimals(original) + restored = restore_decimals(uint_value) + assert restored == original + + # Convert decimal value and restore + original = 0.01 + uint_value = remove_decimals(original) + restored = restore_decimals(uint_value) + assert restored == original + + # Convert Decimal value and restore + original = Decimal('0.01') + uint_value = remove_decimals(original) + restored = restore_decimals(uint_value) + assert float(original) == restored \ No newline at end of file diff --git a/tests/utils/test_types.py b/tests/utils/test_types.py new file mode 100644 index 0000000..09081ee --- /dev/null +++ b/tests/utils/test_types.py @@ -0,0 +1,110 @@ +import pytest +from jpyc_sdk.utils.types import Uint256, Uint8 + +class TestUint256: + def test_init_with_valid_values(self): + # Test with positive integer + value = Uint256(100) + assert value.to_int() == 100 + assert str(value) == "100" + assert repr(value) == "Uint256(100)" + + # Test with zero + value = Uint256(0) + assert value.to_int() == 0 + + # Test with maximum value (2^256-1) + max_value = 2**256 - 1 + value = Uint256(max_value) + assert value.to_int() == max_value + + def test_init_with_invalid_values(self): + # Test with negative number + with pytest.raises(ValueError) as excinfo: + Uint256(-1) + assert "outside the range of uint256" in str(excinfo.value) + + # Test with out-of-range value + with pytest.raises(ValueError) as excinfo: + Uint256(2**256) + assert "outside the range of uint256" in str(excinfo.value) + + # Test with non-integer value + with pytest.raises(TypeError) as excinfo: + Uint256("100") + assert "Expected int" in str(excinfo.value) + + def test_from_string(self): + # Test with valid string + value = Uint256.from_string("100") + assert value.to_int() == 100 + + # Test with invalid string + with pytest.raises(ValueError) as excinfo: + Uint256.from_string("not a number") + assert "Cannot convert" in str(excinfo.value) + + def test_from_value(self): + # Test with integer + value = Uint256.from_value(100) + assert value.to_int() == 100 + + # Test with string + value = Uint256.from_value("100") + assert value.to_int() == 100 + + # Test with invalid type + with pytest.raises(TypeError) as excinfo: + Uint256.from_value(1.5) + assert "Expected int or str" in str(excinfo.value) + + def test_comparison(self): + a = Uint256(100) + b = Uint256(100) + c = Uint256(200) + + assert a == b + assert a == 100 + assert a != c + assert a != 200 + +class TestUint8: + def test_init_with_valid_values(self): + # Test with positive integer + value = Uint8(123) + assert value.to_int() == 123 + assert repr(value) == "Uint8(123)" + + # Test with zero + value = Uint8(0) + assert value.to_int() == 0 + + # Test with maximum value (255) + value = Uint8(255) + assert value.to_int() == 255 + + def test_init_with_invalid_values(self): + # Test with negative number + with pytest.raises(ValueError) as excinfo: + Uint8(-1) + assert "outside the range of uint8" in str(excinfo.value) + + # Test with out-of-range value + with pytest.raises(ValueError) as excinfo: + Uint8(256) + assert "outside the range of uint8" in str(excinfo.value) + + # Test with non-integer value + with pytest.raises(TypeError) as excinfo: + Uint8("100") + assert "Expected int" in str(excinfo.value) + + def test_comparison(self): + a = Uint8(123) + b = Uint8(123) + c = Uint8(200) + + assert a == b + assert a == 123 + assert a != c + assert a != 200 \ No newline at end of file