diff --git a/.env.example b/.env.example index ab9f233f..f72d4d1e 100644 --- a/.env.example +++ b/.env.example @@ -38,3 +38,7 @@ DEPOSIT_MODULES_WHITELIST=1 # Prefix for the Prometheus metrics(depositor_bot,pauser_bot,unvetter_bot) PROMETHEUS_PREFIX=depositor_bot + +# Mellow strategy address for direct deposits +# Holesky: 0x182Cb3A76B0EFaCb25255F9594B5807460882fa4 +MELLOW_CONTRACT_ADDRESS=0x182Cb3A76B0EFaCb25255F9594B5807460882fa4 diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 3c3b46d4..b4466cc4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Python 3.11 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: '3.11' + python-version: '3.12' - name: Setup poetry run: | @@ -29,7 +29,18 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - - name: Integration tests with pytest + - name: Integration tests with pytest, holesky fork + run: | + poetry run pytest tests -m integration_holesky + env: + WEB3_RPC_ENDPOINTS: ${{ secrets.HOLESKY_WEB3_RPC_ENDPOINT }} + DEPOSIT_CONTRACT: "0x4242424242424242424242424242424242424242" + LIDO_LOCATOR: "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" + MELLOW_CONTRACT_ADDRESS: "0x182Cb3A76B0EFaCb25255F9594B5807460882fa4" + ANVIL_PATH: "" + + - name: Integration tests with pytest, mainnet fork + if: success() || failure() run: | poetry run pytest tests -m integration env: diff --git a/.github/workflows/tests-and-checks.yml b/.github/workflows/tests-and-checks.yml index 9b2d7319..2f12042f 100644 --- a/.github/workflows/tests-and-checks.yml +++ b/.github/workflows/tests-and-checks.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Python 3.11 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: '3.11' + python-version: '3.12' - name: Setup poetry run: | diff --git a/Dockerfile b/Dockerfile index 66ef1eb5..c9eaa0b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11.9-slim as base +FROM python:3.12.4-slim as base RUN apt-get update && apt-get install -y --no-install-recommends -qq \ gcc=4:12.2.0-3 \ diff --git a/README.md b/README.md index 8b9f75b6..7070aa86 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Depositor and pauser bots are parts of [Deposit Security Module](https://github. Once a sufficient number of messages is collected to constitute a quorum, the bot proceeds to initiate a deposit into the designated staking module. This deposit is executed using the depositBufferedEther function within the "DepositSecurityModule" smart contract. +Direct deposit is a mechanism that allows depositors to use side vault facilities for deposits. This process transfers ETH from the vault and facilitates the deposit to specified in side vault staking module, preventing funds from being stuck in the withdrawal queue. + **The Pauser Bot** obtains pause message from Council Daemon and enacts pause deposits on protocol. Pause can occurs when Lido detects stealing. **The Unvetting Bot** obtains unvet message from Council Daemon and enacts unvet on the specified node operator. @@ -82,6 +84,8 @@ Unvetting is the proces of decreasing approved depositable signing keys. | PROMETHEUS_PREFIX | depositor_bot | Prefix for the metrics | | HEALTHCHECK_SERVER_PORT | 9010 | Port with bot`s status server | | MAX_CYCLE_LIFETIME_IN_SECONDS | 1200 | Max lifetime of usual cycle. If cycle will not end in this time, bot will crush | +| MELLOW_CONTRACT_ADDRESS | None | If variable is set then deposit can go to predifined module | +| VAULT_DIRECT_DEPOSIT_THRESHOLD | 1 ether | If mellow vault has VAULT_DIRECT_DEPOSIT_THRESHOLD ethers then direct deposit will be sent | ## Metrics and logs @@ -119,11 +123,13 @@ poetry run pytest tests -m unit #### Run integration tests. Install Anvil + ```bash poetry run pytest tests -m integration ``` -In case of "command not found: anvil" error, provide `ANVIL_PATH` variable +In case of "command not found: anvil" error, provide `ANVIL_PATH` variable + ```bash export ANVIL_PATH='pathto/anvil' ``` diff --git a/interfaces/ERC20.json b/interfaces/ERC20.json new file mode 100644 index 00000000..61f835db --- /dev/null +++ b/interfaces/ERC20.json @@ -0,0 +1 @@ +[{"constant": true, "inputs": [], "name": "name", "outputs": [{"name": "", "type": "string"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [{"name": "_spender", "type": "address"}, {"name": "_value", "type": "uint256"}], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [], "name": "totalSupply", "outputs": [{"name": "", "type": "uint256"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [{"name": "_from", "type": "address"}, {"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"}], "name": "transferFrom", "outputs": [{"name": "", "type": "bool"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": true, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": true, "inputs": [], "name": "symbol", "outputs": [{"name": "", "type": "string"}], "payable": false, "stateMutability": "view", "type": "function"}, {"constant": false, "inputs": [{"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"}], "name": "transfer", "outputs": [{"name": "", "type": "bool"}], "payable": false, "stateMutability": "nonpayable", "type": "function"}, {"constant": true, "inputs": [{"name": "_owner", "type": "address"}, {"name": "_spender", "type": "address"}], "name": "allowance", "outputs": [{"name": "", "type": "uint256"}], "payable": false, "stateMutability": "view", "type": "function"}, {"payable": true, "stateMutability": "payable", "type": "fallback"}, {"anonymous": false, "inputs": [{"indexed": true, "name": "owner", "type": "address"}, {"indexed": true, "name": "spender", "type": "address"}, {"indexed": false, "name": "value", "type": "uint256"}], "name": "Approval", "type": "event"}, {"anonymous": false, "inputs": [{"indexed": true, "name": "from", "type": "address"}, {"indexed": true, "name": "to", "type": "address"}, {"indexed": false, "name": "value", "type": "uint256"}], "name": "Transfer", "type": "event"}] diff --git a/interfaces/SimpleDVTStakingStrategy.json b/interfaces/SimpleDVTStakingStrategy.json new file mode 100644 index 00000000..5ff4a9d4 --- /dev/null +++ b/interfaces/SimpleDVTStakingStrategy.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"contract IVault","name":"vault_","type":"address"},{"internalType":"contract IStakingModule","name":"stakingModule_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"DepositFailed","type":"error"},{"inputs":[],"name":"Forbidden","type":"error"},{"inputs":[],"name":"InvalidWithdrawalQueueState","type":"error"},{"inputs":[],"name":"LimitOverflow","type":"error"},{"inputs":[],"name":"NotEnoughWeth","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"ConvertAndDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMaxAllowedRemainder","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"MaxAllowedRemainderChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"users","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"amountForStake","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"ProcessWithdrawals","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"ADMIN_DELEGATE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"depositRoot","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"depositCalldata","type":"bytes"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"vs","type":"bytes32"}],"internalType":"struct IDepositSecurityModule.Signature[]","name":"sortedGuardianSignatures","type":"tuple[]"}],"name":"convertAndDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAllowedRemainder","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint256","name":"amountForStake","type":"uint256"}],"name":"processWithdrawals","outputs":[{"internalType":"bool[]","name":"statuses","type":"bool[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"requireAdmin","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"requireAtLeastOperator","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxAllowedRemainder","type":"uint256"}],"name":"setMaxAllowedRemainder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingModule","outputs":[{"internalType":"contract IStakingModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/StakingModule.json b/interfaces/StakingModule.json new file mode 100644 index 00000000..799b0fa7 --- /dev/null +++ b/interfaces/StakingModule.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"weth_","type":"address"},{"internalType":"address","name":"steth_","type":"address"},{"internalType":"address","name":"wsteth_","type":"address"},{"internalType":"contract ILidoLocator","name":"lidoLocator_","type":"address"},{"internalType":"contract IWithdrawalQueue","name":"withdrawalQueue_","type":"address"},{"internalType":"uint256","name":"stakingModuleId_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"Forbidden","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidDepositRoot","type":"error"},{"inputs":[],"name":"InvalidWithdrawalQueueState","type":"error"},{"inputs":[],"name":"NotEnoughWeth","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Converted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"DepositCompleted","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"depositRoot","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"depositCalldata","type":"bytes"},{"components":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"vs","type":"bytes32"}],"internalType":"struct IDepositSecurityModule.Signature[]","name":"sortedGuardianSignatures","type":"tuple[]"}],"name":"convertAndDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lidoLocator","outputs":[{"internalType":"contract ILidoLocator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingModuleId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"steth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalQueue","outputs":[{"internalType":"contract IWithdrawalQueue","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wsteth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] diff --git a/interfaces/WithdrawalQueue.json b/interfaces/WithdrawalQueue.json new file mode 100644 index 00000000..2c1dd832 --- /dev/null +++ b/interfaces/WithdrawalQueue.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_wstETH","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AdminZeroAddress","type":"error"},{"inputs":[],"name":"ApprovalToOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[{"internalType":"uint256","name":"_firstArrayLength","type":"uint256"},{"internalType":"uint256","name":"_secondArrayLength","type":"uint256"}],"name":"ArraysLengthMismatch","type":"error"},{"inputs":[],"name":"BatchesAreNotSorted","type":"error"},{"inputs":[],"name":"CantSendValueRecipientMayHaveReverted","type":"error"},{"inputs":[],"name":"EmptyBatches","type":"error"},{"inputs":[],"name":"InvalidContractVersionIncrement","type":"error"},{"inputs":[{"internalType":"uint256","name":"_hint","type":"uint256"}],"name":"InvalidHint","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"InvalidOwnerAddress","type":"error"},{"inputs":[],"name":"InvalidReportTimestamp","type":"error"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"InvalidRequestId","type":"error"},{"inputs":[{"internalType":"uint256","name":"startId","type":"uint256"},{"internalType":"uint256","name":"endId","type":"uint256"}],"name":"InvalidRequestIdRange","type":"error"},{"inputs":[],"name":"InvalidState","type":"error"},{"inputs":[],"name":"NonZeroContractVersionOnInit","type":"error"},{"inputs":[],"name":"NotEnoughEther","type":"error"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"NotOwner","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"NotOwnerOrApproved","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"NotOwnerOrApprovedForAll","type":"error"},{"inputs":[],"name":"PauseUntilMustBeInFuture","type":"error"},{"inputs":[],"name":"PausedExpected","type":"error"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"RequestAlreadyClaimed","type":"error"},{"inputs":[{"internalType":"uint256","name":"_amountOfStETH","type":"uint256"}],"name":"RequestAmountTooLarge","type":"error"},{"inputs":[{"internalType":"uint256","name":"_amountOfStETH","type":"uint256"}],"name":"RequestAmountTooSmall","type":"error"},{"inputs":[],"name":"RequestIdsNotSorted","type":"error"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"RequestNotFoundOrNotFinalized","type":"error"},{"inputs":[],"name":"ResumedExpected","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[{"internalType":"uint256","name":"sent","type":"uint256"},{"internalType":"uint256","name":"maxExpected","type":"uint256"}],"name":"TooMuchEtherToFinalize","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"realOwner","type":"address"}],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferFromZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"TransferToNonIERC721Receiver","type":"error"},{"inputs":[],"name":"TransferToThemselves","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"received","type":"uint256"}],"name":"UnexpectedContractVersion","type":"error"},{"inputs":[],"name":"ZeroAmountOfETH","type":"error"},{"inputs":[],"name":"ZeroMetadata","type":"error"},{"inputs":[],"name":"ZeroPauseDuration","type":"error"},{"inputs":[],"name":"ZeroRecipient","type":"error"},{"inputs":[],"name":"ZeroShareRate","type":"error"},{"inputs":[],"name":"ZeroTimestamp","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"baseURI","type":"string"}],"name":"BaseURISet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[],"name":"BunkerModeDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_sinceTimestamp","type":"uint256"}],"name":"BunkerModeEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"}],"name":"ContractVersionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_admin","type":"address"}],"name":"InitializedV1","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"nftDescriptorAddress","type":"address"}],"name":"NftDescriptorAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[],"name":"Resumed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountOfETH","type":"uint256"}],"name":"WithdrawalClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":true,"internalType":"address","name":"requestor","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountOfStETH","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOfShares","type":"uint256"}],"name":"WithdrawalRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"from","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"to","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOfETHLocked","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesToBurn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"WithdrawalsFinalized","type":"event"},{"inputs":[],"name":"BUNKER_MODE_DISABLED_TIMESTAMP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FINALIZE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGE_TOKEN_URI_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BATCHES_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_STETH_WITHDRAWAL_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_STETH_WITHDRAWAL_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ORACLE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_INFINITELY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESUME_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STETH","outputs":[{"internalType":"contract IStETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WSTETH","outputs":[{"internalType":"contract IWstETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bunkerModeSinceTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxShareRate","type":"uint256"},{"internalType":"uint256","name":"_maxTimestamp","type":"uint256"},{"internalType":"uint256","name":"_maxRequestsPerCall","type":"uint256"},{"components":[{"internalType":"uint256","name":"remainingEthBudget","type":"uint256"},{"internalType":"bool","name":"finished","type":"bool"},{"internalType":"uint256[36]","name":"batches","type":"uint256[36]"},{"internalType":"uint256","name":"batchesLength","type":"uint256"}],"internalType":"struct WithdrawalQueueBase.BatchesCalculationState","name":"_state","type":"tuple"}],"name":"calculateFinalizationBatches","outputs":[{"components":[{"internalType":"uint256","name":"remainingEthBudget","type":"uint256"},{"internalType":"bool","name":"finished","type":"bool"},{"internalType":"uint256[36]","name":"batches","type":"uint256[36]"},{"internalType":"uint256","name":"batchesLength","type":"uint256"}],"internalType":"struct WithdrawalQueueBase.BatchesCalculationState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"claimWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_hints","type":"uint256[]"}],"name":"claimWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_hints","type":"uint256[]"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"claimWithdrawalsTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lastRequestIdToBeFinalized","type":"uint256"},{"internalType":"uint256","name":"_maxShareRate","type":"uint256"}],"name":"finalize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"},{"internalType":"uint256","name":"_firstIndex","type":"uint256"},{"internalType":"uint256","name":"_lastIndex","type":"uint256"}],"name":"findCheckpointHints","outputs":[{"internalType":"uint256[]","name":"hintIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBaseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_hints","type":"uint256[]"}],"name":"getClaimableEther","outputs":[{"internalType":"uint256[]","name":"claimableEthValues","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getContractVersion","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastCheckpointIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastFinalizedRequestId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastRequestId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLockedEtherAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNFTDescriptorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getResumeSinceTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"getWithdrawalRequests","outputs":[{"internalType":"uint256[]","name":"requestsIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_requestIds","type":"uint256[]"}],"name":"getWithdrawalStatus","outputs":[{"components":[{"internalType":"uint256","name":"amountOfStETH","type":"uint256"},{"internalType":"uint256","name":"amountOfShares","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"isFinalized","type":"bool"},{"internalType":"bool","name":"isClaimed","type":"bool"}],"internalType":"struct WithdrawalQueueBase.WithdrawalRequestStatus[]","name":"statuses","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isBunkerModeActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_isBunkerModeNow","type":"bool"},{"internalType":"uint256","name":"_bunkerStartTimestamp","type":"uint256"},{"internalType":"uint256","name":"_currentReportTimestamp","type":"uint256"}],"name":"onOracleReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"pauseFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pauseUntilInclusive","type":"uint256"}],"name":"pauseUntil","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_batches","type":"uint256[]"},{"internalType":"uint256","name":"_maxShareRate","type":"uint256"}],"name":"prefinalize","outputs":[{"internalType":"uint256","name":"ethToLock","type":"uint256"},{"internalType":"uint256","name":"sharesToBurn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"}],"name":"requestWithdrawals","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"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"}],"internalType":"struct WithdrawalQueue.PermitInput","name":"_permit","type":"tuple"}],"name":"requestWithdrawalsWithPermit","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"}],"name":"requestWithdrawalsWstETH","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"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"}],"internalType":"struct WithdrawalQueue.PermitInput","name":"_permit","type":"tuple"}],"name":"requestWithdrawalsWstETHWithPermit","outputs":[{"internalType":"uint256[]","name":"requestIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_requestId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_nftDescriptorAddress","type":"address"}],"name":"setNFTDescriptorAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_requestId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unfinalizedRequestNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unfinalizedStETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/poetry.lock b/poetry.lock index c6f87e3b..241b239c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aiohttp" @@ -273,13 +273,13 @@ files = [ [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -539,63 +539,63 @@ schema-registry = ["requests"] [[package]] name = "coverage" -version = "7.5.1" +version = "7.5.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, - {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, - {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, - {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, - {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, - {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, - {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, - {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, - {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, - {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, - {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, - {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, - {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, - {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, + {file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"}, + {file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"}, + {file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"}, + {file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"}, + {file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"}, + {file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"}, + {file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"}, + {file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"}, + {file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"}, + {file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"}, + {file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"}, + {file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"}, + {file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"}, + {file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"}, + {file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"}, + {file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"}, + {file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"}, + {file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"}, + {file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"}, + {file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"}, + {file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"}, + {file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"}, ] [package.dependencies] @@ -859,15 +859,18 @@ test = ["eth-hash[pycryptodome]", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] name = "eth-typing" -version = "4.2.3" +version = "4.3.1" description = "eth-typing: Common type annotations for ethereum python packages" optional = false python-versions = "<4,>=3.8" files = [ - {file = "eth_typing-4.2.3-py3-none-any.whl", hash = "sha256:b2df49fa89d2e85f2cc3fb1c903b0cd183d524f7a045e3db8cc720cf41adcd3d"}, - {file = "eth_typing-4.2.3.tar.gz", hash = "sha256:8ee3ae7d4136d14fcb955c34f9dbef8e52170984d4dc68c0ab0d61621eab29d8"}, + {file = "eth_typing-4.3.1-py3-none-any.whl", hash = "sha256:b4d7cee912c7779da75da4b42fa61475c1089d35a4df5081a786eaa29d5f6865"}, + {file = "eth_typing-4.3.1.tar.gz", hash = "sha256:4504559c87a9f71f4b99aa5a1e0549adaa7f192cbf8e37a295acfcddb1b5412d"}, ] +[package.dependencies] +typing-extensions = ">=4.5.0" + [package.extras] dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "ipython", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] @@ -1264,18 +1267,15 @@ files = [ [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.1" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "numpy" version = "1.26.4" @@ -1323,13 +1323,13 @@ files = [ [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -1377,22 +1377,22 @@ twisted = ["twisted"] [[package]] name = "protobuf" -version = "5.26.1" +version = "5.27.2" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.26.1-cp310-abi3-win32.whl", hash = "sha256:3c388ea6ddfe735f8cf69e3f7dc7611e73107b60bdfcf5d0f024c3ccd3794e23"}, - {file = "protobuf-5.26.1-cp310-abi3-win_amd64.whl", hash = "sha256:e6039957449cb918f331d32ffafa8eb9255769c96aa0560d9a5bf0b4e00a2a33"}, - {file = "protobuf-5.26.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:38aa5f535721d5bb99861166c445c4105c4e285c765fbb2ac10f116e32dcd46d"}, - {file = "protobuf-5.26.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fbfe61e7ee8c1860855696e3ac6cfd1b01af5498facc6834fcc345c9684fb2ca"}, - {file = "protobuf-5.26.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f7417703f841167e5a27d48be13389d52ad705ec09eade63dfc3180a959215d7"}, - {file = "protobuf-5.26.1-cp38-cp38-win32.whl", hash = "sha256:d693d2504ca96750d92d9de8a103102dd648fda04540495535f0fec7577ed8fc"}, - {file = "protobuf-5.26.1-cp38-cp38-win_amd64.whl", hash = "sha256:9b557c317ebe6836835ec4ef74ec3e994ad0894ea424314ad3552bc6e8835b4e"}, - {file = "protobuf-5.26.1-cp39-cp39-win32.whl", hash = "sha256:b9ba3ca83c2e31219ffbeb9d76b63aad35a3eb1544170c55336993d7a18ae72c"}, - {file = "protobuf-5.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ee014c2c87582e101d6b54260af03b6596728505c79f17c8586e7523aaa8f8c"}, - {file = "protobuf-5.26.1-py3-none-any.whl", hash = "sha256:da612f2720c0183417194eeaa2523215c4fcc1a1949772dc65f05047e08d5932"}, - {file = "protobuf-5.26.1.tar.gz", hash = "sha256:8ca2a1d97c290ec7b16e4e5dff2e5ae150cc1582f55b5ab300d45cb0dfa90e51"}, + {file = "protobuf-5.27.2-cp310-abi3-win32.whl", hash = "sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38"}, + {file = "protobuf-5.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505"}, + {file = "protobuf-5.27.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5"}, + {file = "protobuf-5.27.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b"}, + {file = "protobuf-5.27.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e"}, + {file = "protobuf-5.27.2-cp38-cp38-win32.whl", hash = "sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863"}, + {file = "protobuf-5.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6"}, + {file = "protobuf-5.27.2-cp39-cp39-win32.whl", hash = "sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca"}, + {file = "protobuf-5.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce"}, + {file = "protobuf-5.27.2-py3-none-any.whl", hash = "sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470"}, + {file = "protobuf-5.27.2.tar.gz", hash = "sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714"}, ] [[package]] @@ -1438,13 +1438,13 @@ files = [ [[package]] name = "pyright" -version = "1.1.362" +version = "1.1.369" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.362-py3-none-any.whl", hash = "sha256:969957cff45154d8a45a4ab1dae5bdc8223d8bd3c64654fa608ab3194dfff319"}, - {file = "pyright-1.1.362.tar.gz", hash = "sha256:6a477e448d4a07a6a0eab58b2a15a1bbed031eb3169fa809edee79cca168d83a"}, + {file = "pyright-1.1.369-py3-none-any.whl", hash = "sha256:06d5167a8d7be62523ced0265c5d2f1e022e110caf57a25d92f50fb2d07bcda0"}, + {file = "pyright-1.1.369.tar.gz", hash = "sha256:ad290710072d021e213b98cc7a2f90ae3a48609ef5b978f749346d1a47eb9af8"}, ] [package.dependencies] @@ -1456,13 +1456,13 @@ dev = ["twine (>=3.4.1)"] [[package]] name = "pytest" -version = "8.2.0" +version = "8.2.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"}, - {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"}, + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [package.dependencies] @@ -1544,101 +1544,101 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2024.5.10" +version = "2024.5.15" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.5.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eda3dd46df535da787ffb9036b5140f941ecb91701717df91c9daf64cabef953"}, - {file = "regex-2024.5.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d5bd666466c8f00a06886ce1397ba8b12371c1f1c6d1bef11013e9e0a1464a8"}, - {file = "regex-2024.5.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32e5f3b8e32918bfbdd12eca62e49ab3031125c454b507127ad6ecbd86e62fca"}, - {file = "regex-2024.5.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:534efd2653ebc4f26fc0e47234e53bf0cb4715bb61f98c64d2774a278b58c846"}, - {file = "regex-2024.5.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:193b7c6834a06f722f0ce1ba685efe80881de7c3de31415513862f601097648c"}, - {file = "regex-2024.5.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:160ba087232c5c6e2a1e7ad08bd3a3f49b58c815be0504d8c8aacfb064491cd8"}, - {file = "regex-2024.5.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:951be1eae7b47660412dc4938777a975ebc41936d64e28081bf2e584b47ec246"}, - {file = "regex-2024.5.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8a0f0ab5453e409586b11ebe91c672040bc804ca98d03a656825f7890cbdf88"}, - {file = "regex-2024.5.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9e6d4d6ae1827b2f8c7200aaf7501c37cf3f3896c86a6aaf2566448397c823dd"}, - {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:161a206c8f3511e2f5fafc9142a2cc25d7fe9a1ec5ad9b4ad2496a7c33e1c5d2"}, - {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:44b3267cea873684af022822195298501568ed44d542f9a2d9bebc0212e99069"}, - {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:560278c9975694e1f0bc50da187abf2cdc1e4890739ea33df2bc4a85eeef143e"}, - {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:70364a097437dd0a90b31cd77f09f7387ad9ac60ef57590971f43b7fca3082a5"}, - {file = "regex-2024.5.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:42be5de7cc8c1edac55db92d82b68dc8e683b204d6f5414c5a51997a323d7081"}, - {file = "regex-2024.5.10-cp310-cp310-win32.whl", hash = "sha256:9a8625849387b9d558d528e263ecc9c0fbde86cfa5c2f0eef43fff480ae24d71"}, - {file = "regex-2024.5.10-cp310-cp310-win_amd64.whl", hash = "sha256:903350bf44d7e4116b4d5898b30b15755d61dcd3161e3413a49c7db76f0bee5a"}, - {file = "regex-2024.5.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bf9596cba92ce7b1fd32c7b07c6e3212c7eed0edc271757e48bfcd2b54646452"}, - {file = "regex-2024.5.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:45cc13d398b6359a7708986386f72bd156ae781c3e83a68a6d4cee5af04b1ce9"}, - {file = "regex-2024.5.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad45f3bccfcb00868f2871dce02a755529838d2b86163ab8a246115e80cfb7d6"}, - {file = "regex-2024.5.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d19f0cde6838c81acffff25c7708e4adc7dd02896c9ec25c3939b1500a1778"}, - {file = "regex-2024.5.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a9f89d7db5ef6bdf53e5cc8e6199a493d0f1374b3171796b464a74ebe8e508a"}, - {file = "regex-2024.5.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c6c71cf92b09e5faa72ea2c68aa1f61c9ce11cb66fdc5069d712f4392ddfd00"}, - {file = "regex-2024.5.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7467ad8b0eac0b28e52679e972b9b234b3de0ea5cee12eb50091d2b68145fe36"}, - {file = "regex-2024.5.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc0db93ad039fc2fe32ccd3dd0e0e70c4f3d6e37ae83f0a487e1aba939bd2fbd"}, - {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fa9335674d7c819674467c7b46154196c51efbaf5f5715187fd366814ba3fa39"}, - {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7dda3091838206969c2b286f9832dff41e2da545b99d1cfaea9ebd8584d02708"}, - {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:504b5116e2bd1821efd815941edff7535e93372a098e156bb9dffde30264e798"}, - {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:91b53dea84415e8115506cc62e441a2b54537359c63d856d73cb1abe05af4c9a"}, - {file = "regex-2024.5.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a3903128f9e17a500618e80c68165c78c741ebb17dd1a0b44575f92c3c68b02"}, - {file = "regex-2024.5.10-cp311-cp311-win32.whl", hash = "sha256:236cace6c1903effd647ed46ce6dd5d76d54985fc36dafc5256032886736c85d"}, - {file = "regex-2024.5.10-cp311-cp311-win_amd64.whl", hash = "sha256:12446827f43c7881decf2c126762e11425de5eb93b3b0d8b581344c16db7047a"}, - {file = "regex-2024.5.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:14905ed75c7a6edf423eb46c213ed3f4507c38115f1ed3c00f4ec9eafba50e58"}, - {file = "regex-2024.5.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4fad420b14ae1970a1f322e8ae84a1d9d89375eb71e1b504060ab2d1bfe68f3c"}, - {file = "regex-2024.5.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c46a76a599fcbf95f98755275c5527304cc4f1bb69919434c1e15544d7052910"}, - {file = "regex-2024.5.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0faecb6d5779753a6066a3c7a0471a8d29fe25d9981ca9e552d6d1b8f8b6a594"}, - {file = "regex-2024.5.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aab65121229c2ecdf4a31b793d99a6a0501225bd39b616e653c87b219ed34a49"}, - {file = "regex-2024.5.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50e7e96a527488334379e05755b210b7da4a60fc5d6481938c1fa053e0c92184"}, - {file = "regex-2024.5.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba034c8db4b264ef1601eb33cd23d87c5013b8fb48b8161debe2e5d3bd9156b0"}, - {file = "regex-2024.5.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:031219782d97550c2098d9a68ce9e9eaefe67d2d81d8ff84c8354f9c009e720c"}, - {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62b5f7910b639f3c1d122d408421317c351e213ca39c964ad4121f27916631c6"}, - {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cd832bd9b6120d6074f39bdfbb3c80e416848b07ac72910f1c7f03131a6debc3"}, - {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:e91b1976358e17197157b405cab408a5f4e33310cda211c49fc6da7cffd0b2f0"}, - {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:571452362d552de508c37191b6abbbb660028b8b418e2d68c20779e0bc8eaaa8"}, - {file = "regex-2024.5.10-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5253dcb0bfda7214523de58b002eb0090cb530d7c55993ce5f6d17faf953ece7"}, - {file = "regex-2024.5.10-cp312-cp312-win32.whl", hash = "sha256:2f30a5ab8902f93930dc6f627c4dd5da2703333287081c85cace0fc6e21c25af"}, - {file = "regex-2024.5.10-cp312-cp312-win_amd64.whl", hash = "sha256:3799e36d60a35162bb35b2246d8bb012192b7437dff807ef79c14e7352706306"}, - {file = "regex-2024.5.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bbdc5db2c98ac2bf1971ffa1410c87ca7a15800415f788971e8ba8520fc0fda9"}, - {file = "regex-2024.5.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6ccdeef4584450b6f0bddd5135354908dacad95425fcb629fe36d13e48b60f32"}, - {file = "regex-2024.5.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:29d839829209f3c53f004e1de8c3113efce6d98029f044fa5cfee666253ee7e6"}, - {file = "regex-2024.5.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0709ba544cf50bd5cb843df4b8bb6701bae2b70a8e88da9add8386cbca5c1385"}, - {file = "regex-2024.5.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:972b49f2fe1047b9249c958ec4fa1bdd2cf8ce305dc19d27546d5a38e57732d8"}, - {file = "regex-2024.5.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cdbb1998da94607d5eec02566b9586f0e70d6438abf1b690261aac0edda7ab6"}, - {file = "regex-2024.5.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7c8ee4861d9ef5b1120abb75846828c811f932d63311596ad25fa168053e00"}, - {file = "regex-2024.5.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d35d4cc9270944e95f9c88af757b0c9fc43f396917e143a5756608462c5223b"}, - {file = "regex-2024.5.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8722f72068b3e1156a4b2e1afde6810f1fc67155a9fa30a4b9d5b4bc46f18fb0"}, - {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:696639a73ca78a380acfaa0a1f6dd8220616a99074c05bba9ba8bb916914b224"}, - {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea057306ab469130167014b662643cfaed84651c792948891d003cf0039223a5"}, - {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b43b78f9386d3d932a6ce5af4b45f393d2e93693ee18dc4800d30a8909df700e"}, - {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c43395a3b7cc9862801a65c6994678484f186ce13c929abab44fb8a9e473a55a"}, - {file = "regex-2024.5.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0bc94873ba11e34837bffd7e5006703abeffc4514e2f482022f46ce05bd25e67"}, - {file = "regex-2024.5.10-cp38-cp38-win32.whl", hash = "sha256:1118ba9def608250250f4b3e3f48c62f4562ba16ca58ede491b6e7554bfa09ff"}, - {file = "regex-2024.5.10-cp38-cp38-win_amd64.whl", hash = "sha256:458d68d34fb74b906709735c927c029e62f7d06437a98af1b5b6258025223210"}, - {file = "regex-2024.5.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:15e593386ec6331e0ab4ac0795b7593f02ab2f4b30a698beb89fbdc34f92386a"}, - {file = "regex-2024.5.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ca23b41355ba95929e9505ee04e55495726aa2282003ed9b012d86f857d3e49b"}, - {file = "regex-2024.5.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c8982ee19ccecabbaeac1ba687bfef085a6352a8c64f821ce2f43e6d76a9298"}, - {file = "regex-2024.5.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7117cb7d6ac7f2e985f3d18aa8a1728864097da1a677ffa69e970ca215baebf1"}, - {file = "regex-2024.5.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66421f8878a0c82fc0c272a43e2121c8d4c67cb37429b764f0d5ad70b82993b"}, - {file = "regex-2024.5.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:224a9269f133564109ce668213ef3cb32bc72ccf040b0b51c72a50e569e9dc9e"}, - {file = "regex-2024.5.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab98016541543692a37905871a5ffca59b16e08aacc3d7d10a27297b443f572d"}, - {file = "regex-2024.5.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d27844763c273a122e08a3e86e7aefa54ee09fb672d96a645ece0454d8425e"}, - {file = "regex-2024.5.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:853cc36e756ff673bf984e9044ccc8fad60b95a748915dddeab9488aea974c73"}, - {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4e7eaf9df15423d07b6050fb91f86c66307171b95ea53e2d87a7993b6d02c7f7"}, - {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:169fd0acd7a259f58f417e492e93d0e15fc87592cd1e971c8c533ad5703b5830"}, - {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:334b79ce9c08f26b4659a53f42892793948a613c46f1b583e985fd5a6bf1c149"}, - {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f03b1dbd4d9596dd84955bb40f7d885204d6aac0d56a919bb1e0ff2fb7e1735a"}, - {file = "regex-2024.5.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfa6d61a76c77610ba9274c1a90a453062bdf6887858afbe214d18ad41cf6bde"}, - {file = "regex-2024.5.10-cp39-cp39-win32.whl", hash = "sha256:249fbcee0a277c32a3ce36d8e36d50c27c968fdf969e0fbe342658d4e010fbc8"}, - {file = "regex-2024.5.10-cp39-cp39-win_amd64.whl", hash = "sha256:0ce56a923f4c01d7568811bfdffe156268c0a7aae8a94c902b92fe34c4bde785"}, - {file = "regex-2024.5.10.tar.gz", hash = "sha256:304e7e2418146ae4d0ef0e9ffa28f881f7874b45b4994cc2279b21b6e7ae50c8"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6"}, + {file = "regex-2024.5.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796"}, + {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f"}, + {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53"}, + {file = "regex-2024.5.15-cp310-cp310-win32.whl", hash = "sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3"}, + {file = "regex-2024.5.15-cp310-cp310-win_amd64.whl", hash = "sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656"}, + {file = "regex-2024.5.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f"}, + {file = "regex-2024.5.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d"}, + {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68"}, + {file = "regex-2024.5.15-cp311-cp311-win32.whl", hash = "sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa"}, + {file = "regex-2024.5.15-cp311-cp311-win_amd64.whl", hash = "sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e"}, + {file = "regex-2024.5.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf"}, + {file = "regex-2024.5.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d"}, + {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80"}, + {file = "regex-2024.5.15-cp312-cp312-win32.whl", hash = "sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe"}, + {file = "regex-2024.5.15-cp312-cp312-win_amd64.whl", hash = "sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850"}, + {file = "regex-2024.5.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa"}, + {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67"}, + {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741"}, + {file = "regex-2024.5.15-cp38-cp38-win32.whl", hash = "sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9"}, + {file = "regex-2024.5.15-cp38-cp38-win_amd64.whl", hash = "sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1"}, + {file = "regex-2024.5.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c"}, + {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d"}, + {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456"}, + {file = "regex-2024.5.15-cp39-cp39-win32.whl", hash = "sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694"}, + {file = "regex-2024.5.15-cp39-cp39-win_amd64.whl", hash = "sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388"}, + {file = "regex-2024.5.15.tar.gz", hash = "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c"}, ] [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1781,28 +1781,28 @@ files = [ [[package]] name = "ruff" -version = "0.4.4" +version = "0.4.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, - {file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, - {file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, - {file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, - {file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, - {file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, + {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, + {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"}, + {file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"}, + {file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"}, + {file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"}, + {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, ] [[package]] @@ -1816,22 +1816,6 @@ files = [ {file = "schema-0.7.7.tar.gz", hash = "sha256:7da553abd2958a19dc2547c388cde53398b39196175a9be59ea1caf5ab0a1807"}, ] -[[package]] -name = "setuptools" -version = "69.5.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - [[package]] name = "tomli" version = "2.0.1" @@ -1856,24 +1840,24 @@ files = [ [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -1884,13 +1868,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "web3" -version = "6.18.0" +version = "6.20.0" description = "web3.py" optional = false python-versions = ">=3.7.2" files = [ - {file = "web3-6.18.0-py3-none-any.whl", hash = "sha256:86484a3d390a0a024002d1c1b79af27034488c470ea07693ff0f5bf109d3540b"}, - {file = "web3-6.18.0.tar.gz", hash = "sha256:2e626a4bf151171f5dc8ad7f30c373f0416dc2aca9d8d102a63578a2413efa26"}, + {file = "web3-6.20.0-py3-none-any.whl", hash = "sha256:ec09882d21378b688210cf29385e82b604bdc18fe5c2e238bf3b5fe2a6e6dbbc"}, + {file = "web3-6.20.0.tar.gz", hash = "sha256:b04725517502cad4f15e39356eaf7c4fcb0127c7728f83eec8cbafb5b6455f33"}, ] [package.dependencies] @@ -2132,5 +2116,5 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" -python-versions = ">=3.10,<3.12" -content-hash = "5e04ffcd67627598fea768ef176f9a4faa124a237a4dc409ff5fdd890ac04b8a" +python-versions = ">=3.10,<3.13" +content-hash = "eef0557b09fcadb3828cbc33f034544ceac397d31a26589210e31d0672efd721" diff --git a/pyproject.toml b/pyproject.toml index eeb53acc..88469c31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ include = [ ] [tool.poetry.dependencies] -python = ">=3.10,<3.12" +python = ">=3.10,<3.13" numpy = "1.26.4" prometheus-client = "0.20.0" web3-multi-provider = "^0.6.0" @@ -27,12 +27,13 @@ pyright = "^1.1.362" [tool.pytest.ini_options] pythonpath = [ - "src", - "tests", + "src", + "tests", ] markers = [ "unit", # offline "integration", # hardhat fork + "integration_holesky" # holesky fork ] [tool.ruff] diff --git a/src/blockchain/contracts/deposit_security_module.py b/src/blockchain/contracts/deposit_security_module.py index 04e9e33a..10244720 100644 --- a/src/blockchain/contracts/deposit_security_module.py +++ b/src/blockchain/contracts/deposit_security_module.py @@ -77,7 +77,7 @@ def deposit_buffered_ether( logger.info( { 'msg': f'Build `depositBufferedEther({block_number}, {block_hash}, {deposit_root}, {staking_module_id}, ' - f'{nonce}, {deposit_call_data}, {guardian_signatures})` tx.' # noqa + f'{nonce}, {deposit_call_data}, {guardian_signatures})` tx.' # noqa } ) return tx @@ -219,7 +219,7 @@ def unvet_signing_keys( logger.info( { 'msg': f'Build `unvetSigningKeys({block_number}, {block_hash}, {staking_module_id}, {nonce}, ' - f'{operator_ids}, {vetted_keys_by_operator}, {guardian_signature})` tx.' # noqa + f'{operator_ids}, {vetted_keys_by_operator}, {guardian_signature})` tx.' # noqa } ) return tx diff --git a/src/blockchain/contracts/erc20.py b/src/blockchain/contracts/erc20.py new file mode 100644 index 00000000..0ebc0b7d --- /dev/null +++ b/src/blockchain/contracts/erc20.py @@ -0,0 +1,16 @@ +import logging + +from blockchain.contracts.base_interface import ContractInterface +from eth_typing import BlockIdentifier, ChecksumAddress +from web3.types import Wei + +logger = logging.getLogger(__name__) + + +class ERC20Contract(ContractInterface): + abi_path = './interfaces/ERC20.json' + + def balance_of(self, address: ChecksumAddress, block_identifier: BlockIdentifier = 'latest') -> Wei: + response = self.functions.balanceOf(address).call(block_identifier=block_identifier) + logger.info({'msg': 'Call `balanceOf()`.', 'value': response, 'block_identifier': repr(block_identifier)}) + return response diff --git a/src/blockchain/contracts/lido.py b/src/blockchain/contracts/lido.py index ec49a608..4f9fdd8d 100644 --- a/src/blockchain/contracts/lido.py +++ b/src/blockchain/contracts/lido.py @@ -17,3 +17,11 @@ def get_depositable_ether(self, block_identifier: BlockIdentifier = 'latest') -> response = self.functions.getDepositableEther().call(block_identifier=block_identifier) logger.info({'msg': 'Call `getDepositableEther()`.', 'value': response, 'block_identifier': repr(block_identifier)}) return response + + def get_buffered_ether(self, block_identifier: BlockIdentifier = 'latest') -> Wei: + """ + Get the amount of Ether temporary buffered on this contract balance. + """ + response = self.functions.getBufferedEther().call(block_identifier=block_identifier) + logger.info({'msg': 'Call `getBufferedEther()`.', 'value': response, 'block_identifier': repr(block_identifier)}) + return response diff --git a/src/blockchain/contracts/lido_locator.py b/src/blockchain/contracts/lido_locator.py index 48a27a81..38b2a843 100644 --- a/src/blockchain/contracts/lido_locator.py +++ b/src/blockchain/contracts/lido_locator.py @@ -1,6 +1,8 @@ import logging +from typing import cast from blockchain.contracts.base_interface import ContractInterface +from blockchain.contracts.withdrawal_queue import WithdrawalQueueContract from eth_typing import ChecksumAddress from web3.types import BlockIdentifier @@ -24,3 +26,18 @@ def staking_router(self, block_identifier: BlockIdentifier = 'latest') -> Checks response = self.functions.stakingRouter().call(block_identifier=block_identifier) logger.info({'msg': 'Call `stakingRouter()`.', 'value': response, 'block_identifier': repr(block_identifier)}) return response + + def withdrawal_queue(self, block_identifier: BlockIdentifier = 'latest') -> ChecksumAddress: + response = self.functions.withdrawalQueue().call(block_identifier=block_identifier) + logger.info({'msg': 'Call `withdrawalQueue()`.', 'value': response, 'block_identifier': repr(block_identifier)}) + return response + + @property + def withdrawal_queue_contract(self) -> WithdrawalQueueContract: + return cast( + WithdrawalQueueContract, + self.w3.eth.contract( + address=self.withdrawal_queue(), + ContractFactoryClass=WithdrawalQueueContract, + ), + ) diff --git a/src/blockchain/contracts/simple_dvt_staking_strategy.py b/src/blockchain/contracts/simple_dvt_staking_strategy.py new file mode 100644 index 00000000..c65c3e51 --- /dev/null +++ b/src/blockchain/contracts/simple_dvt_staking_strategy.py @@ -0,0 +1,63 @@ +import logging +from typing import cast + +from blockchain.contracts.base_interface import ContractInterface +from blockchain.contracts.staking_module import StakingModuleContract +from eth_typing import BlockIdentifier, ChecksumAddress, Hash32 +from web3.contract.contract import ContractFunction +from web3.types import Wei + +logger = logging.getLogger(__name__) + + +class SimpleDVTStakingStrategyContract(ContractInterface): + abi_path = './interfaces/SimpleDVTStakingStrategy.json' + + def vault(self, block_identifier: BlockIdentifier = 'latest') -> ChecksumAddress: + response = self.functions.vault().call(block_identifier=block_identifier) + logger.info({'msg': 'Call `vault()`.', 'value': response, 'block_identifier': repr(block_identifier)}) + return response + + def get_staking_module(self, block_identifier: BlockIdentifier = 'latest') -> ChecksumAddress: + response = self.functions.stakingModule().call(block_identifier=block_identifier) + logger.info({'msg': 'Call `stakingModule()`.', 'value': response, 'block_identifier': repr(block_identifier)}) + return response + + def convert_and_deposit( + self, + block_number: int, + block_hash: Hash32, + deposit_root: Hash32, + nonce: int, + deposit_call_data: bytes, + guardian_signatures: tuple[tuple[str, str], ...] + ) -> ContractFunction: + tx = self.functions.convertAndDeposit( + block_number, + block_hash, + deposit_root, + nonce, + deposit_call_data, + guardian_signatures, + ) + logger.info( + { + 'msg': f'Build `convertAndDeposit({block_number}, {block_hash}, {deposit_root}, ' + f'{nonce}, {deposit_call_data}, {guardian_signatures})` tx.' # noqa + } + ) + return tx + + @property + def staking_module_contract(self) -> StakingModuleContract: + return cast( + StakingModuleContract, + self.w3.eth.contract( + address=self.get_staking_module(), + ContractFactoryClass=StakingModuleContract, + ) + ) + + def vault_balance(self) -> Wei: + vault_address = self.vault() + return self.staking_module_contract.weth_contract.balance_of(vault_address) diff --git a/src/blockchain/contracts/staking_module.py b/src/blockchain/contracts/staking_module.py new file mode 100644 index 00000000..7a83c8f0 --- /dev/null +++ b/src/blockchain/contracts/staking_module.py @@ -0,0 +1,32 @@ +import logging +from typing import cast + +from blockchain.contracts.base_interface import ContractInterface +from blockchain.contracts.erc20 import ERC20Contract +from eth_typing import BlockIdentifier, ChecksumAddress + +logger = logging.getLogger(__name__) + + +class StakingModuleContract(ContractInterface): + abi_path = './interfaces/StakingModule.json' + + def weth(self, block_identifier: BlockIdentifier = 'latest') -> ChecksumAddress: + response = self.functions.weth().call(block_identifier=block_identifier) + logger.info({'msg': 'Call `weth()`.', 'value': response, 'block_identifier': repr(block_identifier)}) + return response + + def get_staking_module_id(self, block_identifier: BlockIdentifier = 'latest') -> int: + response = self.functions.stakingModuleId().call(block_identifier=block_identifier) + logger.info({'msg': 'Call `stakingModuleId()`.', 'value': response, 'block_identifier': repr(block_identifier)}) + return response + + @property + def weth_contract(self) -> ERC20Contract: + return cast( + ERC20Contract, + self.w3.eth.contract( + address=self.weth(), + ContractFactoryClass=ERC20Contract, + ), + ) diff --git a/src/blockchain/contracts/withdrawal_queue.py b/src/blockchain/contracts/withdrawal_queue.py new file mode 100644 index 00000000..e3d99b59 --- /dev/null +++ b/src/blockchain/contracts/withdrawal_queue.py @@ -0,0 +1,18 @@ +import logging + +from blockchain.contracts.base_interface import ContractInterface +from web3.types import BlockIdentifier, Wei + +logger = logging.getLogger(__name__) + + +class WithdrawalQueueContract(ContractInterface): + abi_path = './interfaces/WithdrawalQueue.json' + + def unfinalized_st_eth(self, block_identifier: BlockIdentifier = 'latest') -> Wei: + """ + Returns unfinalizedStETH ether amount. + """ + response = self.functions.unfinalizedStETH().call(block_identifier=block_identifier) + logger.info({'msg': 'Call `unfinalizedStETH()`.', 'value': response, 'block_identifier': repr(block_identifier)}) + return response diff --git a/src/blockchain/deposit_strategy/base_deposit_strategy.py b/src/blockchain/deposit_strategy/base_deposit_strategy.py new file mode 100644 index 00000000..4a635df7 --- /dev/null +++ b/src/blockchain/deposit_strategy/base_deposit_strategy.py @@ -0,0 +1,46 @@ +import logging + +from blockchain.typings import Web3 +from metrics.metrics import DEPOSITABLE_ETHER, POSSIBLE_DEPOSITS_AMOUNT +from web3.types import Wei + +logger = logging.getLogger(__name__) + + +class BaseDepositStrategy: + """ + Attributes: + DEPOSITABLE_KEYS_THRESHOLD: If the Staking Module has at least THRESHOLD amount of depositable keys, deposits are allowed + """ + DEPOSITABLE_KEYS_THRESHOLD = 1 + + def __init__(self, w3: Web3): + self.w3 = w3 + + def _depositable_ether(self) -> Wei: + depositable_ether = self.w3.lido.lido.get_depositable_ether() + DEPOSITABLE_ETHER.set(depositable_ether) + return depositable_ether + + def deposited_keys_amount(self, module_id: int) -> int: + depositable_ether = self._depositable_ether() + possible_deposits_amount = self.w3.lido.staking_router.get_staking_module_max_deposits_count( + module_id, + depositable_ether, + ) + POSSIBLE_DEPOSITS_AMOUNT.labels(module_id).set(possible_deposits_amount) + return possible_deposits_amount + + +class MellowDepositStrategy(BaseDepositStrategy): + """ + Performs deposited keys amount check for direct deposits. + """ + + def _depositable_ether(self) -> Wei: + depositable_ether = super()._depositable_ether() + additional_ether = self.w3.lido.simple_dvt_staking_strategy.vault_balance() + if additional_ether > 0: + logger.info({'msg': 'Adding mellow vault balance to the depositable check', 'vault': additional_ether}) + depositable_ether += additional_ether + return depositable_ether diff --git a/src/blockchain/deposit_strategy/curated_module.py b/src/blockchain/deposit_strategy/curated_module.py deleted file mode 100644 index ff62cadc..00000000 --- a/src/blockchain/deposit_strategy/curated_module.py +++ /dev/null @@ -1,117 +0,0 @@ -# pyright: reportTypedDictNotRequiredAccess=false - -import logging -from typing import Literal - -import numpy -import variables -from blockchain.deposit_strategy.interface import ModuleDepositStrategyInterface -from blockchain.typings import Web3 -from eth_typing import BlockNumber -from metrics.metrics import DEPOSITABLE_ETHER, GAS_FEE, POSSIBLE_DEPOSITS_AMOUNT -from web3.types import Wei - -logger = logging.getLogger(__name__) - - -class CuratedModuleDepositStrategy(ModuleDepositStrategyInterface): - BLOCKS_IN_ONE_DAY = 24 * 60 * 60 // 12 - - CACHE_BLOCK_AMOUNT = 300 - REQUEST_SIZE = 1024 - - def __init__(self, w3: Web3, module_id: int): - super().__init__(w3, module_id) - - self._gas_fees: list = [] - - # Used for caching - self._latest_fetched_block: int = 0 - self._days_param: int = 0 - - def is_deposited_keys_amount_ok(self) -> bool: - possible_deposits_amount = self._get_possible_deposits_amount() - - if possible_deposits_amount == 0: - logger.info({'msg': f'Possible deposits amount is {possible_deposits_amount}. Skip deposit.'}) - return False - - recommended_max_gas = self._calculate_recommended_gas_based_on_deposit_amount(possible_deposits_amount) - - base_fee_per_gas = self._get_pending_base_fee() - return recommended_max_gas >= base_fee_per_gas - - def _get_possible_deposits_amount(self) -> int: - depositable_ether = self.w3.lido.lido.get_depositable_ether() - DEPOSITABLE_ETHER.labels(self.module_id).set(depositable_ether) - - possible_deposits_amount = self.w3.lido.staking_router.get_staking_module_max_deposits_count( - self.module_id, - depositable_ether, - ) - POSSIBLE_DEPOSITS_AMOUNT.labels(self.module_id).set(possible_deposits_amount) - return possible_deposits_amount - - def _calculate_recommended_gas_based_on_deposit_amount(self, deposits_amount: int) -> int: - # For one key recommended gas fee will be around 10 - # For 10 keys around 100 gwei. For 20 keys ~ 800 gwei - # ToDo percentiles for all modules? - recommended_max_gas = (deposits_amount**3 + 100) * 10**8 - logger.info({'msg': 'Calculate recommended max gas based on possible deposits.', 'value': recommended_max_gas}) - GAS_FEE.labels('based_on_buffer_fee', self.module_id).set(recommended_max_gas) - return recommended_max_gas - - def _get_pending_base_fee(self) -> Wei: - base_fee_per_gas = self.w3.eth.get_block('pending')['baseFeePerGas'] - logger.info({'msg': 'Fetch base_fee_per_gas for pending block.', 'value': base_fee_per_gas}) - return base_fee_per_gas - - def is_gas_price_ok(self) -> bool: - current_gas_fee = self._get_pending_base_fee() - GAS_FEE.labels('current_fee', self.module_id).set(current_gas_fee) - - recommended_gas_fee = self._get_recommended_gas_fee() - GAS_FEE.labels('recommended_fee', self.module_id).set(recommended_gas_fee) - - GAS_FEE.labels('max_fee', self.module_id).set(variables.MAX_GAS_FEE) - - current_buffered_ether = self.w3.lido.lido.get_depositable_ether() - if current_buffered_ether > variables.MAX_BUFFERED_ETHERS: - return current_gas_fee <= variables.MAX_GAS_FEE - - return recommended_gas_fee >= current_gas_fee - - def _get_recommended_gas_fee(self) -> Wei: - gas_history = self._fetch_gas_fee_history(variables.GAS_FEE_PERCENTILE_DAYS_HISTORY_1) - return Wei(int(numpy.percentile(gas_history, variables.GAS_FEE_PERCENTILE_1))) - - def _fetch_gas_fee_history(self, days: int) -> list[int]: - latest_block_num = self.w3.eth.get_block('latest')['number'] - - if ( - self._latest_fetched_block - and self._latest_fetched_block + self.CACHE_BLOCK_AMOUNT > latest_block_num - and self._days_param >= days - ): - logger.info({'msg': 'Use cached gas history'}) - return self._gas_fees - - logger.info({'msg': 'Fetch gas fee history.', 'value': {'block_number': latest_block_num}}) - - self._latest_fetched_block = latest_block_num - self._days_param = days - - total_blocks_to_fetch = self.BLOCKS_IN_ONE_DAY * days - requests_count = total_blocks_to_fetch // self.REQUEST_SIZE + 1 - - gas_fees = [] - last_block: Literal['latest'] | BlockNumber = 'latest' - - for _ in range(requests_count): - stats = self.w3.eth.fee_history(self.REQUEST_SIZE, last_block, []) - last_block = BlockNumber(stats['oldestBlock'] - 2) - gas_fees = stats['baseFeePerGas'] + gas_fees - - self._gas_fees = gas_fees[: days * self.BLOCKS_IN_ONE_DAY] - - return self._gas_fees diff --git a/src/blockchain/deposit_strategy/deposit_transaction_sender.py b/src/blockchain/deposit_strategy/deposit_transaction_sender.py new file mode 100644 index 00000000..1949848c --- /dev/null +++ b/src/blockchain/deposit_strategy/deposit_transaction_sender.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +import logging + +from blockchain.typings import Web3 +from cryptography.verify_signature import compute_vs +from eth_typing import Hash32 +from transport.msg_types.deposit import DepositMessage +from web3.contract.contract import ContractFunction + +logger = logging.getLogger(__name__) + + +class Sender: + """ + Chain senders for deposit transactions. + """ + _TIMEOUT_IN_BLOCKS = 6 + + def __init__(self, w3: Web3): + self._w3 = w3 + + @staticmethod + def _prepare_signs_for_deposit(quorum: list[DepositMessage]) -> tuple[tuple[str, str], ...]: + sorted_messages = sorted(quorum, key=lambda msg: int(msg['guardianAddress'], 16)) + + return tuple((msg['signature']['r'], compute_vs(msg['signature']['v'], msg['signature']['s'])) for msg in sorted_messages) + + def prepare_and_send( + self, + quorum: list[DepositMessage], + with_flashbots: bool, + is_mellow: bool, + ) -> bool: + tx = self._prepare_mellow_tx(quorum) if is_mellow else self._prepare_general_tx(quorum) + return self._send_transaction(tx, with_flashbots) + + def _prepare_mellow_tx(self, quorum: list[DepositMessage]) -> ContractFunction: + block_number = quorum[0]['blockNumber'] + block_hash = Hash32(bytes.fromhex(quorum[0]['blockHash'][2:])) + deposit_root = Hash32(bytes.fromhex(quorum[0]['depositRoot'][2:])) + staking_module_nonce = quorum[0]['nonce'] + payload = b'' + guardian_signs = self._prepare_signs_for_deposit(quorum) + return self._w3.lido.simple_dvt_staking_strategy.convert_and_deposit( + block_number, + block_hash, + deposit_root, + staking_module_nonce, + payload, + guardian_signs, + ) + + def _prepare_general_tx(self, quorum: list[DepositMessage]): + block_number = quorum[0]['blockNumber'] + block_hash = Hash32(bytes.fromhex(quorum[0]['blockHash'][2:])) + deposit_root = Hash32(bytes.fromhex(quorum[0]['depositRoot'][2:])) + staking_module_id = quorum[0]['stakingModuleId'] + staking_module_nonce = quorum[0]['nonce'] + payload = b'' + guardian_signs = self._prepare_signs_for_deposit(quorum) + return self._w3.lido.deposit_security_module.deposit_buffered_ether( + block_number, + block_hash, + deposit_root, + staking_module_id, + staking_module_nonce, + payload, + guardian_signs, + ) + + def _send_transaction( + self, + tx: ContractFunction, + flashbots_works: bool + ) -> bool: + return self._w3.transaction.check(tx) and self._w3.transaction.send(tx, flashbots_works, self._TIMEOUT_IN_BLOCKS) diff --git a/src/blockchain/deposit_strategy/gas_price_calculator.py b/src/blockchain/deposit_strategy/gas_price_calculator.py new file mode 100644 index 00000000..1afa83dd --- /dev/null +++ b/src/blockchain/deposit_strategy/gas_price_calculator.py @@ -0,0 +1,94 @@ +# pyright: reportTypedDictNotRequiredAccess=false + +import logging +from typing import Literal + +import numpy +import variables +from blockchain.deposit_strategy.base_deposit_strategy import BaseDepositStrategy +from blockchain.typings import Web3 +from eth_typing import BlockNumber +from metrics.metrics import GAS_FEE +from web3.types import Wei + +logger = logging.getLogger(__name__) + + +class GasPriceCalculator: + _BLOCKS_IN_ONE_DAY = 24 * 60 * 60 // 12 + _REQUEST_SIZE = 1024 + + def __init__(self, w3: Web3): + self.w3 = w3 + + def is_gas_price_ok(self, module_id: int) -> bool: + """ + Determines if the gas price is ok for doing a deposit. + """ + current_gas_fee = self._get_pending_base_fee() + GAS_FEE.labels('current_fee', module_id).set(current_gas_fee) + + current_buffered_ether = self.w3.lido.lido.get_depositable_ether() + if current_buffered_ether > variables.MAX_BUFFERED_ETHERS: + return current_gas_fee <= variables.MAX_GAS_FEE + + recommended_gas_fee = self._get_recommended_gas_fee() + GAS_FEE.labels('recommended_fee', module_id).set(recommended_gas_fee) + GAS_FEE.labels('max_fee', module_id).set(variables.MAX_GAS_FEE) + return recommended_gas_fee >= current_gas_fee + + def _get_pending_base_fee(self) -> Wei: + base_fee_per_gas = self.w3.eth.get_block('pending')['baseFeePerGas'] + logger.info({'msg': 'Fetch base_fee_per_gas for pending block.', 'value': base_fee_per_gas}) + return base_fee_per_gas + + def calculate_deposit_recommendation(self, deposit_strategy: BaseDepositStrategy, module_id: int) -> bool: + possible_keys = deposit_strategy.deposited_keys_amount(module_id) + if possible_keys < deposit_strategy.DEPOSITABLE_KEYS_THRESHOLD: + logger.info( + { + 'msg': f'Possible deposits amount is {possible_keys}. Skip deposit.', + 'module_id': module_id, + 'threshold': deposit_strategy.DEPOSITABLE_KEYS_THRESHOLD, + } + ) + return False + + recommended_max_gas = GasPriceCalculator._calculate_recommended_gas_based_on_deposit_amount( + possible_keys, + module_id, + ) + base_fee_per_gas = self._get_pending_base_fee() + success = recommended_max_gas >= base_fee_per_gas + logger.info({'msg': 'Calculations deposit recommendations.', 'value': success}) + return success + + @staticmethod + def _calculate_recommended_gas_based_on_deposit_amount(deposits_amount: int, module_id: int) -> int: + # For one key recommended gas fee will be around 10 + # For 10 keys around 100 gwei. For 20 keys ~ 800 gwei + # ToDo percentiles for all modules? + recommended_max_gas = (deposits_amount ** 3 + 100) * 10 ** 8 + logger.info({'msg': 'Calculate recommended max gas based on possible deposits.', 'value': recommended_max_gas}) + GAS_FEE.labels('based_on_buffer_fee', module_id).set(recommended_max_gas) + return recommended_max_gas + + def _get_recommended_gas_fee(self) -> Wei: + gas_history = self._fetch_gas_fee_history(variables.GAS_FEE_PERCENTILE_DAYS_HISTORY_1) + return Wei(int(numpy.percentile(gas_history, variables.GAS_FEE_PERCENTILE_1))) + + def _fetch_gas_fee_history(self, days: int) -> list[int]: + latest_block_num = self.w3.eth.get_block('latest')['number'] + logger.info({'msg': 'Fetch gas fee history.', 'value': {'block_number': latest_block_num}}) + + total_blocks_to_fetch = self._BLOCKS_IN_ONE_DAY * days + requests_count = total_blocks_to_fetch // self._REQUEST_SIZE + 1 + + gas_fees = [] + last_block: Literal['latest'] | BlockNumber = 'latest' + + for _ in range(requests_count): + stats = self.w3.eth.fee_history(self._REQUEST_SIZE, last_block, []) + last_block = BlockNumber(stats['oldestBlock'] - 2) + gas_fees = stats['baseFeePerGas'] + gas_fees + return gas_fees[: days * self._BLOCKS_IN_ONE_DAY] diff --git a/src/blockchain/deposit_strategy/interface.py b/src/blockchain/deposit_strategy/interface.py deleted file mode 100644 index 9dcf8325..00000000 --- a/src/blockchain/deposit_strategy/interface.py +++ /dev/null @@ -1,17 +0,0 @@ -from abc import ABC, abstractmethod - -from blockchain.typings import Web3 - - -class ModuleDepositStrategyInterface(ABC): - def __init__(self, w3: Web3, module_id: int): - self.w3 = w3 - self.module_id = module_id - - @abstractmethod - def is_gas_price_ok(self) -> bool: - pass - - @abstractmethod - def is_deposited_keys_amount_ok(self) -> bool: - pass diff --git a/src/blockchain/web3_extentions/lido_contracts.py b/src/blockchain/web3_extentions/lido_contracts.py index a131b33b..7b17bd52 100644 --- a/src/blockchain/web3_extentions/lido_contracts.py +++ b/src/blockchain/web3_extentions/lido_contracts.py @@ -6,6 +6,7 @@ from blockchain.contracts.deposit_security_module import DepositSecurityModuleContract, DepositSecurityModuleContractV2 from blockchain.contracts.lido import LidoContract from blockchain.contracts.lido_locator import LidoLocatorContract +from blockchain.contracts.simple_dvt_staking_strategy import SimpleDVTStakingStrategyContract from blockchain.contracts.staking_router import StakingRouterContract, StakingRouterContractV2 from web3 import Web3 from web3.contract.contract import Contract @@ -50,10 +51,18 @@ def _load_contracts(self): ContractFactoryClass=LidoContract, ), ) - self._load_staking_router() self._load_dsm() + if variables.MELLOW_CONTRACT_ADDRESS: + self.simple_dvt_staking_strategy: SimpleDVTStakingStrategyContract = cast( + SimpleDVTStakingStrategyContract, + self.w3.eth.contract( + address=variables.MELLOW_CONTRACT_ADDRESS, + ContractFactoryClass=SimpleDVTStakingStrategyContract, + ), + ) + def _load_staking_router(self): staking_router_address = self.lido_locator.staking_router() diff --git a/src/blockchain/web3_extentions/private_relay.py b/src/blockchain/web3_extentions/private_relay.py index 1326bfaa..ee942d2b 100644 --- a/src/blockchain/web3_extentions/private_relay.py +++ b/src/blockchain/web3_extentions/private_relay.py @@ -39,7 +39,7 @@ def send_private_tx(self, tx: SignedTransaction, timeout_in_blocks: int): try: response = self._handle_post_request('eth_sendPrivateTransaction', req_params) except Exception as error: - raise PrivateRelayException from error + raise PrivateRelayException(*error.args) from error if 'result' not in response: raise PrivateRelayException(response.get('error', response)) diff --git a/src/blockchain/web3_extentions/transaction.py b/src/blockchain/web3_extentions/transaction.py index 9282798e..277e01db 100644 --- a/src/blockchain/web3_extentions/transaction.py +++ b/src/blockchain/web3_extentions/transaction.py @@ -4,7 +4,7 @@ import variables from blockchain.constants import SLOT_TIME -from blockchain.web3_extentions.private_relay import PrivateRelayClient +from blockchain.web3_extentions.private_relay import PrivateRelayClient, PrivateRelayException from eth_account.datastructures import SignedTransaction from eth_typing import ChecksumAddress from metrics.metrics import TX_SEND @@ -69,10 +69,14 @@ def send( signed = self.w3.eth.account.sign_transaction(transaction_dict, variables.ACCOUNT._private_key) + status = False if not variables.RELAY_RPC or not variables.AUCTION_BUNDLER_PRIVATE_KEY or not use_relay: status = self.classic_send(signed, timeout_in_blocks) else: - status = self.relay_send(signed, timeout_in_blocks) + try: + status = self.relay_send(signed, timeout_in_blocks) + except PrivateRelayException as error: + logger.error({'msg': 'Private relay error.', 'error': repr(error)}) if status: TX_SEND.labels('success').inc() @@ -102,13 +106,14 @@ def _estimate_gas(transaction: ContractFunction, account_address: ChecksumAddres def relay_send(self, signed_tx: SignedTransaction, timeout_in_blocks: int) -> bool: prl = PrivateRelayClient(self.w3, variables.RELAY_RPC, variables.AUCTION_BUNDLER_PRIVATE_KEY) tx_hash = prl.send_private_tx(signed_tx, timeout_in_blocks) + logger.info({'msg': 'Transaction send to private relay.', 'tx_hash': tx_hash}) try: tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, (timeout_in_blocks + 1) * SLOT_TIME) except TimeExhausted: return False - logger.info({'msg': 'Sent transaction included in blockchain.', 'value': tx_receipt}) + logger.info({'msg': 'Sent transaction included in blockchain.', 'value': repr(tx_receipt)}) return True def classic_send(self, signed_tx: SignedTransaction, timeout_in_blocks: int) -> bool: diff --git a/src/bots/depositor.py b/src/bots/depositor.py index de3635c5..a221bc02 100644 --- a/src/bots/depositor.py +++ b/src/bots/depositor.py @@ -1,20 +1,21 @@ # pyright: reportTypedDictNotRequiredAccess=false - import logging from collections import defaultdict from typing import Callable, Optional import variables -from blockchain.deposit_strategy.curated_module import CuratedModuleDepositStrategy -from blockchain.deposit_strategy.interface import ModuleDepositStrategyInterface +from blockchain.contracts.staking_module import StakingModuleContract +from blockchain.deposit_strategy.base_deposit_strategy import BaseDepositStrategy, MellowDepositStrategy +from blockchain.deposit_strategy.deposit_transaction_sender import Sender +from blockchain.deposit_strategy.gas_price_calculator import GasPriceCalculator from blockchain.deposit_strategy.prefered_module_to_deposit import get_preferred_to_deposit_modules from blockchain.executor import Executor from blockchain.typings import Web3 -from cryptography.verify_signature import compute_vs -from eth_typing import Hash32 from metrics.metrics import ( ACCOUNT_BALANCE, CURRENT_QUORUM_SIZE, + MELLOW_VAULT_BALANCE, + MODULE_TX_SEND, UNEXPECTED_EXCEPTIONS, ) from metrics.transport_message_metrics import message_metrics_filter @@ -32,7 +33,11 @@ def run_depositor(w3): logger.info({'msg': 'Initialize Depositor bot.'}) - depositor_bot = DepositorBot(w3) + gas_price_calculator = GasPriceCalculator(w3) + sender = Sender(w3) + mellow_deposit_strategy = MellowDepositStrategy(w3) + base_deposit_strategy = BaseDepositStrategy(w3) + depositor_bot = DepositorBot(w3, sender, gas_price_calculator, mellow_deposit_strategy, base_deposit_strategy) e = Executor( w3, @@ -50,9 +55,21 @@ class ModuleNotSupportedError(Exception): class DepositorBot: _flashbots_works = True + _mellow_works = True - def __init__(self, w3: Web3): + def __init__( + self, + w3: Web3, + sender: Sender, + gas_price_calcaulator: GasPriceCalculator, + mellow_deposit_strategy: MellowDepositStrategy, + base_deposit_strategy: BaseDepositStrategy, + ): self.w3 = w3 + self._gas_price_calculator = gas_price_calcaulator + self._sender = sender + self._mellow_strategy = mellow_deposit_strategy + self._general_strategy = base_deposit_strategy transports = [] @@ -103,6 +120,44 @@ def execute(self, block: BlockData) -> bool: return True + def _is_mellow_depositable( + self, + module_id: int + ) -> bool: + if not variables.MELLOW_CONTRACT_ADDRESS: + return False + try: + buffered = self.w3.lido.lido.get_buffered_ether() + unfinalized = self.w3.lido.lido_locator.withdrawal_queue_contract.unfinalized_st_eth() + if buffered < unfinalized: + return False + staking_module_contract: StakingModuleContract = self.w3.lido.simple_dvt_staking_strategy.staking_module_contract + if staking_module_contract.get_staking_module_id() != module_id: + logger.debug( + { + 'msg': 'Mellow module check failed.', + 'contract_module': staking_module_contract.get_staking_module_id(), + 'tx_module': module_id + } + ) + return False + balance = self.w3.lido.simple_dvt_staking_strategy.vault_balance() + except Exception as e: + logger.warning( + { + 'msg': 'Failed to check if mellow depositable', + 'module_id': module_id, + 'err': repr(e) + } + ) + return False + MELLOW_VAULT_BALANCE.labels(module_id).set(balance) + if balance < variables.VAULT_DIRECT_DEPOSIT_THRESHOLD: + logger.info({'msg': f'{balance} is less than VAULT_DIRECT_DEPOSIT_THRESHOLD while building mellow transaction.'}) + return False + logger.debug({'msg': 'Mellow module check succeeded.', 'tx_module': module_id}) + return True + def _check_balance(self): if variables.ACCOUNT: balance = self.w3.eth.get_balance(variables.ACCOUNT.address) @@ -122,26 +177,27 @@ def _deposit_to_module(self, module_id: int) -> bool: can_deposit = self.w3.lido.deposit_security_module.can_deposit(module_id) logger.info({'msg': 'Can deposit to module.', 'value': can_deposit}) - module_strategy = self._get_module_strategy(module_id) - - gas_is_ok = module_strategy.is_gas_price_ok() + gas_is_ok = self._gas_price_calculator.is_gas_price_ok(module_id) logger.info({'msg': 'Calculate gas recommendations.', 'value': gas_is_ok}) - keys_amount_is_profitable = module_strategy.is_deposited_keys_amount_ok() - logger.info({'msg': 'Calculations deposit recommendations.', 'value': keys_amount_is_profitable}) + strategy, is_mellow = self._select_strategy(module_id) + is_deposit_amount_ok = self._gas_price_calculator.calculate_deposit_recommendation(strategy, module_id) + logger.info({'msg': 'Calculations deposit recommendations.', 'value': is_deposit_amount_ok}) - if is_depositable and quorum and can_deposit and gas_is_ok and keys_amount_is_profitable: - logger.info({'msg': 'Checks passed. Prepare deposit tx.'}) - return self._build_and_send_deposit_tx(quorum) + if is_depositable and quorum and can_deposit and gas_is_ok and is_deposit_amount_ok: + logger.info({'msg': 'Checks passed. Prepare deposit tx.', 'is_mellow': is_mellow}) + success = self.prepare_and_send_tx(quorum, is_mellow, self._flashbots_works) + self._flashbots_works = not self._flashbots_works or success + self._mellow_works = success + return success logger.info({'msg': 'Checks failed. Skip deposit.'}) return False - def _get_module_strategy(self, module_id: int) -> ModuleDepositStrategyInterface: - if module_id in (1, 2, 3): - return CuratedModuleDepositStrategy(self.w3, module_id) - - raise ModuleNotSupportedError(f'Module with id: {module_id} is not supported yet.') + def _select_strategy(self, module_id) -> tuple[BaseDepositStrategy, bool]: + if self._mellow_works and self._is_mellow_depositable(module_id): + return self._mellow_strategy, True + return self._general_strategy, False def _check_module_status(self, module_id: int) -> bool: """Returns True if module is ready for deposit""" @@ -219,58 +275,13 @@ def message_filter(message: DepositMessage) -> bool: return message_filter - def _build_and_send_deposit_tx(self, quorum: list[DepositMessage]) -> bool: - signs = self._prepare_signs_for_deposit(quorum) - - return self._send_deposit_tx( - quorum[0]['blockNumber'], - Hash32(bytes.fromhex(quorum[0]['blockHash'][2:])), - Hash32(bytes.fromhex(quorum[0]['depositRoot'][2:])), - quorum[0]['stakingModuleId'], - quorum[0]['nonce'], - b'', - signs, - ) - - @staticmethod - def _prepare_signs_for_deposit(quorum: list[DepositMessage]) -> tuple[tuple[str, str], ...]: - sorted_messages = sorted(quorum, key=lambda msg: int(msg['guardianAddress'], 16)) - - return tuple((msg['signature']['r'], compute_vs(msg['signature']['v'], msg['signature']['s'])) for msg in sorted_messages) - - def _send_deposit_tx( - self, - block_number: int, - block_hash: Hash32, - deposit_root: Hash32, - staking_module_id: int, - staking_module_nonce: int, - payload: bytes, - guardian_signs: tuple[tuple[str, str], ...], - ) -> bool: - """Returns transactions success status""" - # Prepare transaction and send - deposit_tx = self.w3.lido.deposit_security_module.deposit_buffered_ether( - block_number, - block_hash, - deposit_root, - staking_module_id, - staking_module_nonce, - payload, - guardian_signs, + def prepare_and_send_tx(self, quorum: list[DepositMessage], is_mellow: bool, module_id: int) -> bool: + success = self._sender.prepare_and_send( + quorum, + self._flashbots_works, + is_mellow, ) - - if not self.w3.transaction.check(deposit_tx): - return False - - logger.info({'msg': 'Send deposit transaction.', 'with_flashbots': self._flashbots_works}) - success = self.w3.transaction.send(deposit_tx, self._flashbots_works, 6) - logger.info({'msg': f'Tx send. Result is {success}.'}) - - if self._flashbots_works and not success: - self._flashbots_works = False - else: - self._flashbots_works = True - + label = 'success' if success else 'failure' + MODULE_TX_SEND.labels(label, module_id).inc() return success diff --git a/src/bots/unvetter.py b/src/bots/unvetter.py index 92820453..211d19f8 100644 --- a/src/bots/unvetter.py +++ b/src/bots/unvetter.py @@ -48,7 +48,7 @@ def prepare_transport_bus(self): transports.append( RabbitProvider( client='unvetter', - routing_keys=[MessageType.UNVET, MessageType.PAUSE], + routing_keys=[MessageType.UNVET, MessageType.PING], message_schema=Schema(Or(UnvetMessageSchema, PingMessageSchema)), ) ) diff --git a/src/main.py b/src/main.py index 97a86ff2..480f1ec1 100644 --- a/src/main.py +++ b/src/main.py @@ -11,7 +11,6 @@ from bots.unvetter import run_unvetter from metrics.healthcheck_pulse import start_pulse_server from metrics.logging import logging -from metrics.metrics import BUILD_INFO from prometheus_client import start_http_server from web3_multi_provider import FallbackProvider @@ -25,6 +24,13 @@ class BotModule(StrEnum): def main(bot_name: str): + logger.info( + { + 'msg': 'Bot env variables', + 'value': variables.PUBLIC_ENV_VARS, + 'bot_name': bot_name + } + ) if bot_name not in list(BotModule): msg = f'Last arg should be one of {[str(item) for item in BotModule]}, received {BotModule}.' logger.error({'msg': msg}) @@ -36,24 +42,9 @@ def main(bot_name: str): logger.info({'msg': f'Start up metrics service on port: {variables.PROMETHEUS_PORT}.'}) start_http_server(variables.PROMETHEUS_PORT) - # Send vars to metrics - BUILD_INFO.labels( - 'Depositor bot', - variables.MAX_GAS_FEE, - variables.MAX_BUFFERED_ETHERS, - variables.CONTRACT_GAS_LIMIT, - variables.GAS_FEE_PERCENTILE_1, - variables.GAS_FEE_PERCENTILE_DAYS_HISTORY_1, - variables.GAS_PRIORITY_FEE_PERCENTILE, - variables.MIN_PRIORITY_FEE, - variables.MAX_PRIORITY_FEE, - variables.ACCOUNT.address if variables.ACCOUNT else '0x0', - variables.CREATE_TRANSACTIONS, - variables.DEPOSIT_MODULES_WHITELIST, - ) - logger.info({'msg': 'Connect MultiHTTPProviders.', 'rpc_count': len(variables.WEB3_RPC_ENDPOINTS)}) w3 = Web3(FallbackProvider(variables.WEB3_RPC_ENDPOINTS)) + logger.info({'msg': 'Current chain_id', 'chain_id': w3.eth.chain_id}) logger.info({'msg': 'Initialize Lido contracts.'}) w3.attach_modules( diff --git a/src/metrics/metrics.py b/src/metrics/metrics.py index 4b4a4022..ea53f88e 100644 --- a/src/metrics/metrics.py +++ b/src/metrics/metrics.py @@ -1,27 +1,5 @@ -from prometheus_client.metrics import Counter, Gauge, Histogram -from variables import PROMETHEUS_PREFIX - -METRICS_PREFIX = PROMETHEUS_PREFIX - -BUILD_INFO = Gauge( - 'build_info', - 'Build info', - [ - 'name', - 'max_gas_fee', - 'max_buffered_ethers', - 'contract_gas_limit', - 'gas_fee_percentile_1', - 'gas_fee_percentile_days_history_1', - 'gas_priority_fee_percentile', - 'min_priority_fee', - 'max_priority_fee', - 'account_address', - 'create_transactions', - 'modules_whitelist', - ], - namespace=PROMETHEUS_PREFIX, -) +from prometheus_client.metrics import Counter, Gauge, Histogram, Info +from variables import DEPOSIT_MODULES_WHITELIST, PROMETHEUS_PREFIX, PUBLIC_ENV_VARS GAS_FEE = Gauge('gas_fee', 'Gas fee', ['type', 'module_id'], namespace=PROMETHEUS_PREFIX) @@ -31,6 +9,13 @@ TX_SEND.labels('success').inc(0) TX_SEND.labels('failure').inc(0) +MODULE_TX_SEND = Counter( + 'transactions', + 'Amount of send transaction from bot with per module distribution.', + ['status', 'module_id'], + namespace=PROMETHEUS_PREFIX +) + ACCOUNT_BALANCE = Gauge('account_balance', 'Account balance', namespace=PROMETHEUS_PREFIX) DEPOSIT_MESSAGES = Gauge( @@ -63,7 +48,7 @@ DEPOSITABLE_ETHER = Gauge( 'depositable_ether', 'Depositable Ether', - ['module_id'], + [], namespace=PROMETHEUS_PREFIX, ) @@ -74,6 +59,13 @@ namespace=PROMETHEUS_PREFIX, ) +MELLOW_VAULT_BALANCE = Gauge( + 'mellow_vault_balance', + 'Mellow vault balance.', + ['module_id'], + namespace=PROMETHEUS_PREFIX, +) + ETH_RPC_REQUESTS_DURATION = Histogram('eth_rpc_requests_duration', 'Duration of requests to ETH1 RPC', namespace=PROMETHEUS_PREFIX) ETH_RPC_REQUESTS = Counter( @@ -86,3 +78,12 @@ ['type'], namespace=PROMETHEUS_PREFIX, ) + +MODULES = Gauge('modules', 'Modules gauge', ['module_id'], namespace=PROMETHEUS_PREFIX) + +for module_id in DEPOSIT_MODULES_WHITELIST: + MODULES.labels(module_id).set(1) + +INFO = Info(name='build', documentation='Info metric', namespace=PROMETHEUS_PREFIX) +CONVERTED_PUBLIC_ENV = {k: str(v) for k, v in PUBLIC_ENV_VARS.items()} +INFO.info(CONVERTED_PUBLIC_ENV) diff --git a/src/variables.py b/src/variables.py index d79b024c..33dcf833 100644 --- a/src/variables.py +++ b/src/variables.py @@ -1,19 +1,21 @@ import logging import os +from typing import Optional from eth_account import Account +from eth_account.signers.local import LocalAccount from eth_typing import URI from web3 import Web3 logger = logging.getLogger(__name__) - # EL node WEB3_RPC_ENDPOINTS = os.getenv('WEB3_RPC_ENDPOINTS', '').split(',') # Account private key WALLET_PRIVATE_KEY = os.getenv('WALLET_PRIVATE_KEY', None) +ACCOUNT: Optional[LocalAccount] if WALLET_PRIVATE_KEY: ACCOUNT = Account.from_key(WALLET_PRIVATE_KEY) logger.info({'msg': 'Load account from private key.', 'value': ACCOUNT.address}) @@ -32,6 +34,16 @@ # Holesky: 0x4242424242424242424242424242424242424242 DEPOSIT_CONTRACT = Web3.to_checksum_address(os.getenv('DEPOSIT_CONTRACT', '0x00000000219ab540356cBB839Cbe05303d7705Fa')) +# Mellow contract address +# Mainnet: TBD +# Holesky: 0x4720ad6b59fb06c5b97b05e8b8f7538071302f00 +MELLOW_CONTRACT_ADDRESS = os.getenv('MELLOW_CONTRACT_ADDRESS', None) +# Can be empty string - then skip +if MELLOW_CONTRACT_ADDRESS: + # bot will throw exception if there is unexpected str and it's ok + MELLOW_CONTRACT_ADDRESS = Web3.to_checksum_address(MELLOW_CONTRACT_ADDRESS) +VAULT_DIRECT_DEPOSIT_THRESHOLD = Web3.to_wei(*os.getenv('VAULT_DIRECT_DEPOSIT_THRESHOLD', '1 ether').split(' ')) + # rabbit / kafka / rabbit,kafka MESSAGE_TRANSPORTS = os.getenv('MESSAGE_TRANSPORTS', '').split(',') @@ -55,7 +67,7 @@ MAX_PRIORITY_FEE = Web3.to_wei(*os.getenv('MAX_PRIORITY_FEE', '10 gwei').split(' ')) MAX_GAS_FEE = Web3.to_wei(*os.getenv('MAX_GAS_FEE', '100 gwei').split(' ')) -CONTRACT_GAS_LIMIT = int(os.getenv('CONTRACT_GAS_LIMIT', 15 * 10**6)) +CONTRACT_GAS_LIMIT = int(os.getenv('CONTRACT_GAS_LIMIT', 15 * 10 ** 6)) # Mainnet: "https://relay.flashbots.net", # Holesky: "https://relay-holesky.flashbots.net", @@ -78,3 +90,44 @@ # List of ids of staking modules in which the depositor bot will make deposits DEPOSIT_MODULES_WHITELIST = [int(module_id) for module_id in os.getenv('DEPOSIT_MODULES_WHITELIST', '1').split(',')] + +# All non-private env variables to the logs in main +PUBLIC_ENV_VARS = { + 'LIDO_LOCATOR': LIDO_LOCATOR, + 'DEPOSIT_CONTRACT': DEPOSIT_CONTRACT, + 'MESSAGE_TRANSPORTS': MESSAGE_TRANSPORTS, + 'CREATE_TRANSACTIONS': CREATE_TRANSACTIONS, + 'MIN_PRIORITY_FEE': MIN_PRIORITY_FEE, + 'MAX_PRIORITY_FEE': MAX_PRIORITY_FEE, + 'MAX_GAS_FEE': MAX_GAS_FEE, + 'RELAY_RPC': RELAY_RPC, + 'GAS_FEE_PERCENTILE_1': GAS_FEE_PERCENTILE_1, + 'GAS_FEE_PERCENTILE_DAYS_HISTORY_1': GAS_FEE_PERCENTILE_DAYS_HISTORY_1, + 'GAS_PRIORITY_FEE_PERCENTILE': GAS_PRIORITY_FEE_PERCENTILE, + 'MAX_BUFFERED_ETHERS': MAX_BUFFERED_ETHERS, + 'PROMETHEUS_PORT': PROMETHEUS_PORT, + 'PROMETHEUS_PREFIX': PROMETHEUS_PREFIX, + 'HEALTHCHECK_SERVER_PORT': HEALTHCHECK_SERVER_PORT, + 'MAX_CYCLE_LIFETIME_IN_SECONDS': MAX_CYCLE_LIFETIME_IN_SECONDS, + 'DEPOSIT_MODULES_WHITELIST': DEPOSIT_MODULES_WHITELIST, + 'ACCOUNT': '' if ACCOUNT is None else ACCOUNT.address, + 'MELLOW_CONTRACT_ADDRESS': MELLOW_CONTRACT_ADDRESS, + 'VAULT_DIRECT_DEPOSIT_THRESHOLD': VAULT_DIRECT_DEPOSIT_THRESHOLD, +} + +PRIVATE_ENV_VARS = { + 'WEB3_RPC_ENDPOINTS': WEB3_RPC_ENDPOINTS, + 'WALLET_PRIVATE_KEY': WALLET_PRIVATE_KEY, + 'KAFKA_BROKER_ADDRESS_1': KAFKA_BROKER_ADDRESS_1, + 'KAFKA_USERNAME': KAFKA_USERNAME, + 'KAFKA_PASSWORD': KAFKA_PASSWORD, + 'KAFKA_NETWORK': KAFKA_NETWORK, + 'KAFKA_TOPIC': KAFKA_TOPIC, + 'KAFKA_GROUP_PREFIX': KAFKA_GROUP_PREFIX, + 'RABBIT_MQ_URL': RABBIT_MQ_URL, + 'RABBIT_MQ_USERNAME': RABBIT_MQ_USERNAME, + 'RABBIT_MQ_PASSWORD': RABBIT_MQ_PASSWORD, + 'AUCTION_BUNDLER_PRIVATE_KEY': AUCTION_BUNDLER_PRIVATE_KEY, +} + +assert not set(PRIVATE_ENV_VARS.keys()).intersection(set(PUBLIC_ENV_VARS.keys())) diff --git a/tests/blockchain/contracts/test_erc20.py b/tests/blockchain/contracts/test_erc20.py new file mode 100644 index 00000000..0778588e --- /dev/null +++ b/tests/blockchain/contracts/test_erc20.py @@ -0,0 +1,15 @@ +import pytest + +from tests.utils.contract_utils import check_contract +from tests.utils.regrex import check_value_type + + +@pytest.mark.integration_holesky +def test_erc20(weth, simple_dvt_staking_strategy, caplog): + check_contract( + weth, + [ + ('balance_of', (simple_dvt_staking_strategy.vault(),), lambda response: check_value_type(response, int)), + ], + caplog, + ) diff --git a/tests/blockchain/contracts/test_simple_dvt_staking_strategy.py b/tests/blockchain/contracts/test_simple_dvt_staking_strategy.py new file mode 100644 index 00000000..1ab70758 --- /dev/null +++ b/tests/blockchain/contracts/test_simple_dvt_staking_strategy.py @@ -0,0 +1,16 @@ +import pytest + +from tests.utils.contract_utils import check_contract +from tests.utils.regrex import ADDRESS_REGREX, check_value_re + + +@pytest.mark.integration_holesky +def test_simple_dvt_staking_strategy_contract_call(simple_dvt_staking_strategy, caplog): + check_contract( + simple_dvt_staking_strategy, + [ + ('get_staking_module', None, lambda response: check_value_re(ADDRESS_REGREX, response)), + ('vault', None, lambda response: check_value_re(ADDRESS_REGREX, response)), + ], + caplog, + ) diff --git a/tests/blockchain/contracts/test_staking_module.py b/tests/blockchain/contracts/test_staking_module.py new file mode 100644 index 00000000..38b8eee0 --- /dev/null +++ b/tests/blockchain/contracts/test_staking_module.py @@ -0,0 +1,16 @@ +import pytest + +from tests.utils.contract_utils import check_contract +from tests.utils.regrex import ADDRESS_REGREX, check_value_re, check_value_type + + +@pytest.mark.integration_holesky +def test_staking_module_contract_call(staking_module, caplog): + check_contract( + staking_module, + [ + ('get_staking_module_id', None, lambda response: check_value_type(response, int)), + ('weth', None, lambda response: check_value_re(ADDRESS_REGREX, response)), + ], + caplog, + ) diff --git a/tests/blockchain/deposit_strategy/test_base_deposit_strategy.py b/tests/blockchain/deposit_strategy/test_base_deposit_strategy.py new file mode 100644 index 00000000..8b01cfb3 --- /dev/null +++ b/tests/blockchain/deposit_strategy/test_base_deposit_strategy.py @@ -0,0 +1,38 @@ +from unittest.mock import Mock + +import pytest + +MODULE_ID = 1 + + +@pytest.mark.unit +def test_deposited_keys_amount(base_deposit_strategy): + depositable_eth = 100 + possible_deposits = depositable_eth // 32 + + base_deposit_strategy.w3.lido.lido.get_depositable_ether = Mock(return_value=depositable_eth) + base_deposit_strategy.w3.lido.staking_router.get_staking_module_max_deposits_count = Mock(return_value=possible_deposits) + + assert base_deposit_strategy.deposited_keys_amount(MODULE_ID) == possible_deposits + base_deposit_strategy.w3.lido.staking_router.get_staking_module_max_deposits_count.assert_called_once_with( + MODULE_ID, + depositable_eth, + ) + + +@pytest.mark.unit +def test_deposited_keys_amount_mellow(mellow_deposit_strategy): + depositable_eth = 100 + possible_deposits = depositable_eth // 32 + vault_balance = 10 + + mellow_deposit_strategy.w3.lido.simple_dvt_staking_strategy.vault_balance = Mock(return_value=vault_balance) + + mellow_deposit_strategy.w3.lido.lido.get_depositable_ether = Mock(return_value=depositable_eth) + mellow_deposit_strategy.w3.lido.staking_router.get_staking_module_max_deposits_count = Mock(return_value=possible_deposits) + + assert mellow_deposit_strategy.deposited_keys_amount(MODULE_ID) == possible_deposits + mellow_deposit_strategy.w3.lido.staking_router.get_staking_module_max_deposits_count.assert_called_once_with( + MODULE_ID, + depositable_eth + vault_balance, + ) diff --git a/tests/blockchain/deposit_strategy/test_common.py b/tests/blockchain/deposit_strategy/test_common.py new file mode 100644 index 00000000..4439d4e0 --- /dev/null +++ b/tests/blockchain/deposit_strategy/test_common.py @@ -0,0 +1,55 @@ +import pytest +from blockchain.deposit_strategy.deposit_transaction_sender import Sender + + +@pytest.fixture +def deposit_message(): + yield { + 'type': 'deposit', + 'depositRoot': '0x64dcf70a7ad7fc6b1a55db6b08b86e9d80736259916fcaef98f4710f0bac687b', + 'nonce': 12, + 'blockNumber': 10, + 'blockHash': '0x432e218931e9b94f0702ecb1b0d084c467a86b384767ce38c4fe164463070532', + 'guardianAddress': '0x43464Fe06c18848a2E2e913194D64c1970f4326a', + 'guardianIndex': 8, + 'stakingModuleId': 1, + 'signature': { + 'r': '0xc2235eb6983f80d19158f807d5d90d93abec52034ea7184bbf164ba211f00116', + 's': '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', + '_vs': '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', + 'recoveryParam': 0, + 'v': 27, + }, + 'app': {'version': '1.0.3', 'name': 'lido-council-daemon'}, + } + + +@pytest.mark.unit +def test_prepare_signs_for_deposit(deposit_message): + second_council = { + 'guardianAddress': '0x13464Fe06c18848a2E2e913194D64c1970f4326a', + 'signature': { + 'r': '0xc2235eb6983f80d19158f807d5d90d93abec52034ea7184bbf164ba211f00116', + 's': '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', + '_vs': '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', + 'recoveryParam': 0, + 'v': 27, + }, + } + + expected = ( + ( + '0xc2235eb6983f80d19158f807d5d90d93abec52034ea7184bbf164ba211f00116', + '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', + ), + ( + '0xc2235eb6983f80d19158f807d5d90d93abec52034ea7184bbf164ba211f00116', + '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', + ), + ) + + signs = Sender._prepare_signs_for_deposit([second_council, deposit_message]) + assert signs == expected + + signs = Sender._prepare_signs_for_deposit([deposit_message, second_council]) + assert signs == expected diff --git a/tests/blockchain/deposit_strategy/test_curated_module.py b/tests/blockchain/deposit_strategy/test_curated_module.py deleted file mode 100644 index b6013de0..00000000 --- a/tests/blockchain/deposit_strategy/test_curated_module.py +++ /dev/null @@ -1,104 +0,0 @@ -from unittest.mock import Mock - -import pytest -import variables -from blockchain.deposit_strategy.curated_module import CuratedModuleDepositStrategy - -MODULE_ID = 1337 - - -@pytest.fixture -def cmds(web3_lido_unit): - yield CuratedModuleDepositStrategy(web3_lido_unit, module_id=MODULE_ID) - - -@pytest.mark.unit -def test_is_deposited_keys_amount_ok(cmds): - cmds._get_possible_deposits_amount = Mock(return_value=100) - - cmds._calculate_recommended_gas_based_on_deposit_amount = Mock(return_value=30) - cmds._get_pending_base_fee = Mock(return_value=20) - - assert cmds.is_deposited_keys_amount_ok() - - cmds._get_pending_base_fee = Mock(return_value=50) - assert not cmds.is_deposited_keys_amount_ok() - - -@pytest.mark.unit -def test_get_possible_deposits_amount(cmds): - depositable_eth = 100 - possible_deposits = depositable_eth // 32 - - cmds.w3.lido.lido.get_depositable_ether = Mock(return_value=depositable_eth) - cmds.w3.lido.staking_router.get_staking_module_max_deposits_count = Mock(return_value=possible_deposits) - - assert cmds._get_possible_deposits_amount() == possible_deposits - cmds.w3.lido.staking_router.get_staking_module_max_deposits_count.assert_called_once_with( - MODULE_ID, - depositable_eth, - ) - - -@pytest.mark.unit -@pytest.mark.parametrize( - 'deposits,expected_range', - [(1, (0, 20)), (5, (20, 100)), (10, (50, 1000)), (100, (1000, 1000000))], -) -def test_calculate_recommended_gas_based_on_deposit_amount(cmds, deposits, expected_range): - assert expected_range[0] * 10**9 <= cmds._calculate_recommended_gas_based_on_deposit_amount(deposits) <= expected_range[1] * 10**9 - - -@pytest.mark.unit -def test_get_recommended_gas_fee(cmds): - cmds._fetch_gas_fee_history = Mock(return_value=list(range(11))) - variables.GAS_FEE_PERCENTILE_DAYS_HISTORY_1 = 1 - variables.GAS_FEE_PERCENTILE_1 = 50 - - assert cmds._get_recommended_gas_fee() == 5 - - variables.GAS_FEE_PERCENTILE_1 = 30 - assert cmds._get_recommended_gas_fee() == 3 - - -@pytest.mark.unit -def test_is_gas_price_ok(cmds): - cmds._get_pending_base_fee = Mock(return_value=10) - cmds._get_recommended_gas_fee = Mock(return_value=20) - variables.MAX_GAS_FEE = 300 - - cmds.w3.lido.lido.get_depositable_ether = Mock(return_value=100) - variables.MAX_BUFFERED_ETHERS = 200 - assert cmds.is_gas_price_ok() - - cmds._get_recommended_gas_fee = Mock(return_value=5) - assert not cmds.is_gas_price_ok() - - cmds.w3.lido.lido.get_depositable_ether = Mock(return_value=300) - assert cmds.is_gas_price_ok() - - cmds._get_pending_base_fee = Mock(return_value=400) - assert not cmds.is_gas_price_ok() - - -@pytest.fixture() -def cmds_integration(web3_lido_integration): - yield CuratedModuleDepositStrategy(web3_lido_integration, module_id=MODULE_ID) - - -@pytest.mark.integration -def test_get_pending_base_fee(cmds_integration): - pending_gas = cmds_integration._get_pending_base_fee() - assert 1 <= pending_gas <= 1000 * 10**9 - - -@pytest.mark.integration -def test_fetch_gas_fee_history(cmds_integration): - history = cmds_integration._fetch_gas_fee_history(1) - assert isinstance(history, list) - assert len(history) == 1 * 24 * 60 * 60 / 12 - - cmds_integration.w3.eth.fee_history = Mock() - cmds_integration._fetch_gas_fee_history(1) - assert len(history) == 1 * 24 * 60 * 60 / 12 - cmds_integration.w3.eth.fee_history.assert_not_called() diff --git a/tests/blockchain/deposit_strategy/test_deposit_transaction_sender.py b/tests/blockchain/deposit_strategy/test_deposit_transaction_sender.py new file mode 100644 index 00000000..eb201c86 --- /dev/null +++ b/tests/blockchain/deposit_strategy/test_deposit_transaction_sender.py @@ -0,0 +1,34 @@ +from unittest.mock import Mock + +import pytest +from transport.msg_types.deposit import DepositMessage + +MODULE_ID = 1 + + +@pytest.mark.unit +def test_send_deposit_tx_not_mellow(deposit_transaction_sender): + deposit_transaction_sender._w3.transaction.check = Mock(return_value=False) + messages = [DepositMessage( + type='deposit', + depositRoot='', + nonce=1, + blockNumber=1, + blockHash='', + guardianAddress='0x43464Fe06c18848a2E2e913194D64c1970f4326a', + stakingModuleId=1, + signature={ + 'r': '0xc2235eb6983f80d19158f807d5d90d93abec52034ea7184bbf164ba211f00116', + 's': '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', + 'v': 27, + }, + app={'version': '1.0.3', 'name': 'lido-council-daemon'}, + )] + deposit_transaction_sender._prepare_signs_for_deposit = Mock(return_value=tuple()) + assert not deposit_transaction_sender.prepare_and_send(messages, False, False) + assert deposit_transaction_sender._w3.lido.deposit_security_module.deposit_buffered_ether.called + + deposit_transaction_sender._w3.transaction.check = Mock(return_value=True) + deposit_transaction_sender._w3.transaction.send = Mock(return_value=True) + assert deposit_transaction_sender.prepare_and_send(messages, False, False) + assert deposit_transaction_sender._w3.lido.deposit_security_module.deposit_buffered_ether.called diff --git a/tests/blockchain/deposit_strategy/test_gas_price_calculator.py b/tests/blockchain/deposit_strategy/test_gas_price_calculator.py new file mode 100644 index 00000000..d72f4bd8 --- /dev/null +++ b/tests/blockchain/deposit_strategy/test_gas_price_calculator.py @@ -0,0 +1,61 @@ +from unittest.mock import Mock + +import pytest +import variables + +MODULE_ID = 1 + + +@pytest.mark.unit +def test_is_gas_price_ok(gas_price_calculator): + gas_price_calculator._get_pending_base_fee = Mock(return_value=10) + gas_price_calculator._get_recommended_gas_fee = Mock(return_value=20) + variables.MAX_GAS_FEE = 300 + + gas_price_calculator.w3.lido.lido.get_depositable_ether = Mock(return_value=100) + variables.MAX_BUFFERED_ETHERS = 200 + assert gas_price_calculator.is_gas_price_ok(MODULE_ID) + + gas_price_calculator._get_recommended_gas_fee = Mock(return_value=5) + assert not gas_price_calculator.is_gas_price_ok(MODULE_ID) + + gas_price_calculator.w3.lido.lido.get_depositable_ether = Mock(return_value=300) + assert gas_price_calculator.is_gas_price_ok(MODULE_ID) + + gas_price_calculator._get_pending_base_fee = Mock(return_value=400) + assert not gas_price_calculator.is_gas_price_ok(MODULE_ID) + + +@pytest.mark.unit +@pytest.mark.parametrize( + 'deposits,expected_range', + [(1, (0, 20)), (5, (20, 100)), (10, (50, 1000)), (100, (1000, 1000000))], +) +def test_calculate_recommended_gas_based_on_deposit_amount(gas_price_calculator, deposits, expected_range): + assert expected_range[0] * 10 ** 9 <= gas_price_calculator._calculate_recommended_gas_based_on_deposit_amount(deposits, MODULE_ID) <= \ + expected_range[1] * 10 ** 9 + + +@pytest.mark.unit +def test_get_recommended_gas_fee(gas_price_calculator): + gas_price_calculator._fetch_gas_fee_history = Mock(return_value=list(range(11))) + variables.GAS_FEE_PERCENTILE_DAYS_HISTORY_1 = 1 + variables.GAS_FEE_PERCENTILE_1 = 50 + + assert gas_price_calculator._get_recommended_gas_fee() == 5 + + variables.GAS_FEE_PERCENTILE_1 = 30 + assert gas_price_calculator._get_recommended_gas_fee() == 3 + + +@pytest.mark.integration +def test_get_pending_base_fee(gas_price_calculator_integration): + pending_gas = gas_price_calculator_integration._get_pending_base_fee() + assert 1 <= pending_gas <= 1000 * 10 ** 9 + + +@pytest.mark.integration +def test_fetch_gas_fee_history(gas_price_calculator_integration): + history = gas_price_calculator_integration._fetch_gas_fee_history(1) + assert isinstance(history, list) + assert len(history) == 1 * 24 * 60 * 60 / 12 diff --git a/tests/bots/test_depositor.py b/tests/bots/test_depositor.py index 659ae986..239e47e6 100644 --- a/tests/bots/test_depositor.py +++ b/tests/bots/test_depositor.py @@ -3,6 +3,7 @@ import pytest import variables +from blockchain.typings import Web3 from bots.depositor import DepositorBot from tests.conftest import DSM_OWNER @@ -15,12 +16,19 @@ @pytest.fixture -def depositor_bot(web3_lido_unit, block_data): +def depositor_bot( + web3_lido_unit, + deposit_transaction_sender, + gas_price_calculator, + mellow_deposit_strategy, + base_deposit_strategy, + block_data, +): variables.MESSAGE_TRANSPORTS = '' variables.DEPOSIT_MODULES_WHITELIST = [1, 2] web3_lido_unit.lido.staking_router.get_staking_module_ids = Mock(return_value=[1, 2]) web3_lido_unit.eth.get_block = Mock(return_value=block_data) - yield DepositorBot(web3_lido_unit) + yield DepositorBot(web3_lido_unit, deposit_transaction_sender, mellow_deposit_strategy, base_deposit_strategy, gas_price_calculator) @pytest.fixture @@ -49,7 +57,7 @@ def deposit_message(): def test_depositor_one_module_deposited(depositor_bot, block_data): modules = list(range(10)) - depositor_bot.w3.lido.lido.get_depositable_ether = Mock(return_value=10 * 32 * 10**18) + depositor_bot.w3.lido.lido.get_depositable_ether = Mock(return_value=10 * 32 * 10 ** 18) depositor_bot.w3.lido.staking_router.get_staking_module_ids = Mock(return_value=modules) depositor_bot.w3.lido.staking_router.get_staking_module_max_deposits_count = Mock(return_value=0) depositor_bot.w3.lido.deposit_security_module.get_max_deposits = Mock(return_value=10) @@ -66,6 +74,28 @@ def test_depositor_one_module_deposited(depositor_bot, block_data): assert depositor_bot._deposit_to_module.call_count == 2 +@pytest.mark.unit +def test_is_mellow_depositable(depositor_bot): + variables.MELLOW_CONTRACT_ADDRESS = None + assert not depositor_bot._is_mellow_depositable(1) + + variables.MELLOW_CONTRACT_ADDRESS = '0x1' + depositor_bot.w3.lido.lido.get_buffered_ether = Mock(return_value=Web3.to_wei(1, 'ether')) + depositor_bot.w3.lido.lido_locator.withdrawal_queue_contract.unfinalized_st_eth = Mock(return_value=Web3.to_wei(1, 'ether')) + depositor_bot.w3.lido.simple_dvt_staking_strategy.staking_module_contract.get_staking_module_id = Mock(return_value=1) + assert not depositor_bot._is_mellow_depositable(2) + + depositor_bot.w3.lido.simple_dvt_staking_strategy.vault_balance = Mock(return_value=Web3.to_wei(0.5, 'ether')) + assert not depositor_bot._is_mellow_depositable(1) + + depositor_bot.w3.lido.simple_dvt_staking_strategy.vault_balance = Mock(return_value=Web3.to_wei(1.4, 'ether')) + assert depositor_bot._is_mellow_depositable(1) + + depositor_bot.w3.lido.lido.get_buffered_ether = Mock(return_value=Web3.to_wei(0.5, 'ether')) + depositor_bot.w3.lido.lido_locator.withdrawal_queue_contract.unfinalized_st_eth = Mock(return_value=Web3.to_wei(1, 'ether')) + assert not depositor_bot._is_mellow_depositable(1) + + @pytest.mark.unit def test_check_balance_dry(depositor_bot, caplog): caplog.set_level(logging.INFO) @@ -77,7 +107,7 @@ def test_check_balance_dry(depositor_bot, caplog): def test_check_balance(depositor_bot, caplog, set_account): caplog.set_level(logging.INFO) - depositor_bot.w3.eth.get_balance = Mock(return_value=10 * 10**18) + depositor_bot.w3.eth.get_balance = Mock(return_value=10 * 10 ** 18) depositor_bot._check_balance() assert 'Check account balance' in caplog.messages[-1] @@ -105,13 +135,9 @@ def test_depositor_check_module_status(depositor_bot): def test_depositor_deposit_to_module(depositor_bot, is_depositable, quorum, is_gas_price_ok, is_deposited_keys_amount_ok): depositor_bot._check_module_status = Mock(return_value=is_depositable) depositor_bot._get_quorum = Mock(return_value=quorum) - - strategy = Mock() - strategy.is_gas_price_ok = Mock(return_value=is_gas_price_ok) - strategy.is_deposited_keys_amount_ok = Mock(return_value=is_deposited_keys_amount_ok) - - depositor_bot._get_module_strategy = Mock(return_value=strategy) - depositor_bot._build_and_send_deposit_tx = Mock(return_value=True) + depositor_bot._mellow_works = False + depositor_bot._gas_price_calculator.is_gas_price_ok = Mock(return_value=is_gas_price_ok) + depositor_bot._gas_price_calculator.calculate_deposit_recommendation = Mock(return_value=is_deposited_keys_amount_ok) assert not depositor_bot._deposit_to_module(1) @@ -187,64 +213,6 @@ def test_depositor_message_actualizer_root(setup_deposit_message, depositor_bot, assert list(filter(message_filter, [deposit_message])) -@pytest.mark.unit -def test_prepare_signs_for_deposit(deposit_message, depositor_bot): - second_council = { - 'guardianAddress': '0x13464Fe06c18848a2E2e913194D64c1970f4326a', - 'signature': { - 'r': '0xc2235eb6983f80d19158f807d5d90d93abec52034ea7184bbf164ba211f00116', - 's': '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', - '_vs': '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', - 'recoveryParam': 0, - 'v': 27, - }, - } - - expected = ( - ( - '0xc2235eb6983f80d19158f807d5d90d93abec52034ea7184bbf164ba211f00116', - '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', - ), - ( - '0xc2235eb6983f80d19158f807d5d90d93abec52034ea7184bbf164ba211f00116', - '0x75354ffc9fb6e7a4b4c01c622661a1d0382ace8c4ff8024626e39ac1a6a613d0', - ), - ) - - signs = depositor_bot._prepare_signs_for_deposit([second_council, deposit_message]) - assert signs == expected - - signs = depositor_bot._prepare_signs_for_deposit([deposit_message, second_council]) - assert signs == expected - - -@pytest.mark.unit -def test_send_deposit_tx(depositor_bot): - depositor_bot.w3.transaction.check = Mock(return_value=False) - params = [ - 1, - b'', - b'', - 1, - 1, - b'', - tuple(), - ] - assert not depositor_bot._send_deposit_tx(*params) - - depositor_bot.w3.transaction.check = Mock(return_value=True) - depositor_bot.w3.transaction.send = Mock(return_value=True) - assert depositor_bot._send_deposit_tx(*params) - assert depositor_bot._flashbots_works - - depositor_bot.w3.transaction.send = Mock(return_value=False) - assert not depositor_bot._send_deposit_tx(*params) - assert not depositor_bot._flashbots_works - - assert not depositor_bot._send_deposit_tx(*params) - assert depositor_bot._flashbots_works - - @pytest.mark.unit def test_get_quorum(depositor_bot, setup_deposit_message): deposit_messages = [ @@ -330,7 +298,16 @@ def add_accounts_to_guardian(web3_lido_integration, set_integration_account): [[19628126, 1], [19628126, 2]], indirect=['web3_provider_integration'], ) -def test_depositor_bot(web3_provider_integration, web3_lido_integration, module_id, add_accounts_to_guardian): +def test_depositor_bot( + web3_provider_integration, + web3_lido_integration, + deposit_transaction_sender_integration, + mellow_deposit_strategy_integration, + base_deposit_strategy_integration, + gas_price_calculator_integration, + module_id, + add_accounts_to_guardian, +): variables.DEPOSIT_MODULES_WHITELIST = [1, 2] web3_lido_integration.provider.make_request( 'anvil_setBalance', @@ -344,7 +321,7 @@ def test_depositor_bot(web3_provider_integration, web3_lido_integration, module_ web3_lido_integration.lido.lido.functions.submit(web3_lido_integration.eth.accounts[0]).transact( { 'from': web3_lido_integration.eth.accounts[0], - 'value': 10000 * 10**18, + 'value': 10000 * 10 ** 18, } ) @@ -360,13 +337,19 @@ def test_depositor_bot(web3_provider_integration, web3_lido_integration, module_ web3_lido_integration.provider.make_request('anvil_mine', [1]) - db = DepositorBot(web3_lido_integration) + db: DepositorBot = DepositorBot( + web3_lido_integration, + deposit_transaction_sender_integration, + gas_price_calculator_integration, + mellow_deposit_strategy_integration, + base_deposit_strategy_integration, + ) + db._mellow_works = False db.message_storage.messages = [] db.execute(latest) assert web3_lido_integration.lido.staking_router.get_staking_module_nonce(module_id) == old_module_nonce db.message_storage.messages = [deposit_message_1, deposit_message_2, deposit_message_3] - db._get_module_strategy = Mock(return_value=Mock(return_value=True)) assert db.execute(latest) assert web3_lido_integration.lido.staking_router.get_staking_module_nonce(module_id) == old_module_nonce + 1 diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index 1e33d6cb..68a0d847 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -4,15 +4,28 @@ deposit_security_module_v2, lido_contract, lido_locator, + simple_dvt_staking_strategy, + staking_module, staking_router, staking_router_v2, upgrade_staking_router_to_v2, + weth, ) from .provider import ( web3_lido_integration, web3_lido_unit, web3_provider_integration, ) +from .strategy import ( + base_deposit_strategy, + base_deposit_strategy_integration, + deposit_transaction_sender, + deposit_transaction_sender_integration, + gas_price_calculator, + gas_price_calculator_integration, + mellow_deposit_strategy, + mellow_deposit_strategy_integration, +) __all__ = [ 'lido_locator', @@ -26,4 +39,15 @@ 'web3_lido_unit', 'web3_provider_integration', 'web3_lido_integration', + 'weth', + 'simple_dvt_staking_strategy', + 'staking_module', + 'base_deposit_strategy', + 'base_deposit_strategy_integration', + 'deposit_transaction_sender', + 'gas_price_calculator', + 'gas_price_calculator_integration', + 'deposit_transaction_sender_integration', + 'mellow_deposit_strategy', + 'mellow_deposit_strategy_integration' ] diff --git a/tests/fixtures/contracts.py b/tests/fixtures/contracts.py index 899363c4..c7fc6ebf 100644 --- a/tests/fixtures/contracts.py +++ b/tests/fixtures/contracts.py @@ -4,8 +4,11 @@ import variables from blockchain.contracts.deposit import DepositContract from blockchain.contracts.deposit_security_module import DepositSecurityModuleContract, DepositSecurityModuleContractV2 +from blockchain.contracts.erc20 import ERC20Contract from blockchain.contracts.lido import LidoContract from blockchain.contracts.lido_locator import LidoLocatorContract +from blockchain.contracts.simple_dvt_staking_strategy import SimpleDVTStakingStrategyContract +from blockchain.contracts.staking_module import StakingModuleContract from blockchain.contracts.staking_router import StakingRouterContract, StakingRouterContractV2 from blockchain.typings import Web3 @@ -88,6 +91,39 @@ def staking_router_v2(web3_provider_integration, lido_locator): ) +@pytest.fixture +def simple_dvt_staking_strategy(web3_provider_integration): + yield cast( + SimpleDVTStakingStrategyContract, + web3_provider_integration.eth.contract( + address=variables.MELLOW_CONTRACT_ADDRESS, + ContractFactoryClass=SimpleDVTStakingStrategyContract, + ), + ) + + +@pytest.fixture +def staking_module(web3_provider_integration, simple_dvt_staking_strategy): + yield cast( + StakingModuleContract, + web3_provider_integration.eth.contract( + address=simple_dvt_staking_strategy.get_staking_module(), + ContractFactoryClass=StakingModuleContract, + ), + ) + + +@pytest.fixture +def weth(web3_provider_integration, staking_module): + yield cast( + ERC20Contract, + web3_provider_integration.eth.contract( + address=staking_module.weth(), + ContractFactoryClass=ERC20Contract, + ), + ) + + DSM_V2_BYTECODE = '0x60a06040527f2dd9727393562ed11c29080a884630e2d3a7078e71b313e713a8a1ef68948f6a608052600360055560e160068190556007819055600855600d805460ff1916905534801561005257600080fd5b50608051611b0361006e60003960006102ed0152611b036000f3fe608060405234801561001057600080fd5b50600436106101ce5760003560e01c80634d72ba8611610104578063b7b7a408116100a2578063c8f712d511610071578063c8f712d5146103b9578063eccd085f146103cc578063f47610e6146103df578063ffa1ad74146103f257600080fd5b8063b7b7a4081461038d578063c6dda2c314610395578063c7062e981461039e578063c8a5f8e6146103a657600080fd5b8063893d20e8116100de578063893d20e81461034d5780638b21f1701461035e5780638d71a6f414610371578063a50833d61461038457600080fd5b80634d72ba86146102e85780636b96736b1461030f5780637e9233121461033a57600080fd5b8063251e3a21116101715780633ab54bc31161014b5780633ab54bc3146102965780633bab964e146102a95780633e6f6d68146102bc5780634726daaf146102cf57600080fd5b8063251e3a211461027057806327042b841461027857806339443b8e1461028357600080fd5b80630c68ba21116101ad5780630c68ba21146102145780630df9a86d14610237578063111e53131461024a57806313af40351461025d57600080fd5b8062fed902146101d3578063062b662e146101e85780630665f04b146101ff575b600080fd5b6101e66101e1366004611562565b6103fb565b005b600a545b6040519081526020015b60405180910390f35b610207610439565b6040516101f6919061157b565b6102276102223660046115e4565b61049b565b60405190151581526020016101f6565b6101e661024536600461164f565b6104bd565b6101e661025836600461171e565b61087c565b6101e661026b3660046115e4565b6108bf565b6007546101ec565b600d5460ff16610227565b610227610291366004611562565b6108f5565b6101e66102a43660046117de565b610b41565b6101e66102b7366004611562565b610b7d565b6101e66102ca366004611562565b610cbc565b6101e66102dd36600461180b565b505050505050505050565b6101ec7f000000000000000000000000000000000000000000000000000000000000000081565b600454610322906001600160a01b031681565b6040516001600160a01b0390911681526020016101f6565b6101e6610348366004611562565b610cf2565b6009546001600160a01b0316610322565b600254610322906001600160a01b031681565b6101e661037f36600461171e565b610d28565b6101ec60015481565b6006546101ec565b6101ec60005481565b6008546101ec565b600354610322906001600160a01b031681565b6101e66103c73660046118bd565b610ed7565b6101e66103da366004611562565b610f4c565b6101ec6103ed3660046115e4565b610f82565b6101ec60055481565b6009546001600160a01b0316331461042d5760405163351c880f60e01b81523360048201526024015b60405180910390fd5b61043681610f8d565b50565b6060600b80548060200260200160405190810160405280929190818152602001828054801561049157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610473575b5050505050905090565b6001600160a01b0381166000908152600c602052604081205415155b92915050565b600a5415806104cd5750600a5481105b156104eb57604051630833b4e360e41b815260040160405180910390fd5b6000600460009054906101000a90046001600160a01b03166001600160a01b031663c5f2892f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561053b57600080fd5b505afa15801561054f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105739190611970565b9050808814610595576040516302d654b160e41b815260040160405180910390fd5b600354604051636608b11b60e01b8152600481018990526001600160a01b0390911690636608b11b9060240160206040518083038186803b1580156105d957600080fd5b505afa1580156105ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106119190611989565b61062e5760405163fa7f287160e01b815260040160405180910390fd5b60035460405163473e043360e01b8152600481018990526000916001600160a01b03169063473e04339060240160206040518083038186803b15801561067357600080fd5b505afa158015610687573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ab9190611970565b6007549091506106bb82436119c1565b10156106da57604051633ec3745360e11b815260040160405180910390fd5b8915806106e85750898b4014155b15610706576040516328aada8b60e21b815260040160405180910390fd5b600354604051630519fbbf60e01b8152600481018a90526000916001600160a01b031690630519fbbf9060240160206040518083038186803b15801561074b57600080fd5b505afa15801561075f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107839190611970565b90508088146107a557604051632e8f7e7360e11b815260040160405180910390fd5b6108058a8d8d8c8c8a8a808060200260200160405190810160405280939291908181526020016000905b828210156107fb576107ec604083028601368190038101906119d8565b815260200190600101906107cf565b5050505050610fc9565b60025460065460405163aa0b7db760e01b81526001600160a01b039092169163aa0b7db79161083c918d908c908c906004016119f4565b600060405180830381600087803b15801561085657600080fd5b505af115801561086a573d6000803e3d6000fd5b50505050505050505050505050505050565b6009546001600160a01b031633146108a95760405163351c880f60e01b8152336004820152602401610424565b6108b2826110ed565b6108bb81611201565b5050565b6009546001600160a01b031633146108ec5760405163351c880f60e01b8152336004820152602401610424565b6104368161123f565b6003546040516329cd0ca760e21b8152600481018390526000916001600160a01b03169063a734329c9060240160206040518083038186803b15801561093a57600080fd5b505afa15801561094e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109729190611989565b61097e57506000919050565b600354604051636608b11b60e01b8152600481018490526000916001600160a01b031690636608b11b9060240160206040518083038186803b1580156109c357600080fd5b505afa1580156109d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109fb9190611989565b60035460405163473e043360e01b8152600481018690529192506000916001600160a01b039091169063473e04339060240160206040518083038186803b158015610a4557600080fd5b505afa158015610a59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a7d9190611970565b90506000600260009054906101000a90046001600160a01b03166001600160a01b031663e78a58756040518163ffffffff1660e01b815260040160206040518083038186803b158015610acf57600080fd5b505afa158015610ae3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b079190611989565b9050828015610b1857506000600a54115b8015610b2f5750600754610b2c83436119c1565b10155b8015610b385750805b95945050505050565b60405133907feb225a736fbfee3f85ccb72bdf84ff0396ab358b7970e2cc351ab3e3fd92358d90600090a25050600d805460ff19166001179055565b6009546001600160a01b03163314610baa5760405163351c880f60e01b8152336004820152602401610424565b60035460405163e24ce9f160e01b8152600481018390526001600160a01b039091169063e24ce9f19060240160206040518083038186803b158015610bee57600080fd5b505afa158015610c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c269190611989565b1561043657600354604051633618716160e21b8152600481018390526001600160a01b039091169063d861c58490602401600060405180830381600087803b158015610c7157600080fd5b505af1158015610c85573d6000803e3d6000fd5b505060405162ffffff841692507fb861fabb6dfce807f39e6f400693b8a826dcd89a4ce4ff8e955f6c4ddec398a39150600090a250565b6009546001600160a01b03163314610ce95760405163351c880f60e01b8152336004820152602401610424565b61043681611201565b6009546001600160a01b03163314610d1f5760405163351c880f60e01b8152336004820152602401610424565b610436816112d0565b6009546001600160a01b03163314610d555760405163351c880f60e01b8152336004820152602401610424565b6001600160a01b0382166000908152600c602052604090205480610d97576040516302333ca160e51b81526001600160a01b0384166004820152602401610424565b600b5480821115610daa57610daa611a31565b808214610e40576000600b610dc06001846119c1565b81548110610dd057610dd0611a47565b6000918252602090912001546001600160a01b0316905080600b610df56001866119c1565b81548110610e0557610e05611a47565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055929091168152600c909152604090208290555b6001600160a01b0384166000908152600c6020526040812055600b805480610e6a57610e6a611a5d565b600082815260209020810160001990810180546001600160a01b0319169055019055610e9583611201565b6040516001600160a01b03851681527fb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c529060200160405180910390a150505050565b6009546001600160a01b03163314610f045760405163351c880f60e01b8152336004820152602401610424565b60005b8251811015610f4257610f32838281518110610f2557610f25611a47565b60200260200101516110ed565b610f3b81611a73565b9050610f07565b506108bb81611201565b6009546001600160a01b03163314610f795760405163351c880f60e01b8152336004820152602401610424565b6104368161135c565b60006104b7826113df565b60068190556040518181527f4d72502b63cfe737b98b225a53708fe347cf8274baed31e0c4e4941b758da992906020015b60405180910390a150565b600080546040805160208101929092528101879052606081018690526080810188905260a0810185905260c0810184905260e0016040516020818303038152906040528051906020012090506000805b83518110156102dd5760006110698486848151811061103a5761103a611a47565b60200260200101516000015187858151811061105857611058611a47565b602002602001015160200151611404565b905061108c816001600160a01b03166000908152600c6020526040902054151590565b6110a957604051638baa579f60e01b815260040160405180910390fd5b826001600160a01b0316816001600160a01b0316116110db576040516301eba55160e01b815260040160405180910390fd5b91506110e681611a73565b9050611019565b6001600160a01b0381166111335760405163eac0d38960e01b815260206004820152600c60248201526b2fb732bba3bab0b93234b0b760a11b6044820152606401610424565b6001600160a01b0381166000908152600c60205260409020541561117557604051639f2277f360e01b81526001600160a01b0382166004820152602401610424565b600b80546001810182557f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b0319166001600160a01b03841690811790915590546000828152600c602090815260409182902092909255519182527f038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f9699101610fbe565b80600a541461043657600a8190556040518181527f70d7432f2ec830b36e5b8c45176a8079968714429c4be85665c06ec1b8fde4bb90602001610fbe565b6001600160a01b0381166112825760405163eac0d38960e01b81526020600482015260096024820152682fb732bba7bbb732b960b91b6044820152606401610424565b600980546001600160a01b0319166001600160a01b0383169081179091556040519081527fa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf3690602001610fbe565b8061131e57604051631fd43b6f60e21b815260206004820152601760248201527f6d696e4465706f736974426c6f636b44697374616e63650000000000000000006044820152606401610424565b60075481146104365760078190556040518181527fdb69cbc4aa6648b506b7854c26807bfd811c27feaf97ac8847e3a66356cace1490602001610fbe565b806113aa57604051631fd43b6f60e21b815260206004820152601f60248201527f7061757365496e74656e7456616c6964697479506572696f64426c6f636b73006044820152606401610424565b60088190556040518181527f8120886d27fee35672e5d5a482d6c858105aebb26caf12178b8c0034fa88c2ba90602001610fbe565b6001600160a01b0381166000908152600c60205260408120546104b790600190611a8e565b60006001600160ff1b03821660ff83901c601b016114248682878561142e565b9695505050505050565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156114ab5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610424565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa1580156114ff573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610b385760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610424565b60006020828403121561157457600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156115bc5783516001600160a01b031683529284019291840191600101611597565b50909695505050505050565b80356001600160a01b03811681146115df57600080fd5b919050565b6000602082840312156115f657600080fd5b6115ff826115c8565b9392505050565b60008083601f84011261161857600080fd5b50813567ffffffffffffffff81111561163057600080fd5b60208301915083602082850101111561164857600080fd5b9250929050565b600080600080600080600080600060e08a8c03121561166d57600080fd5b8935985060208a0135975060408a0135965060608a0135955060808a0135945060a08a013567ffffffffffffffff808211156116a857600080fd5b6116b48d838e01611606565b909650945060c08c01359150808211156116cd57600080fd5b818c0191508c601f8301126116e157600080fd5b8135818111156116f057600080fd5b8d60208260061b850101111561170557600080fd5b6020830194508093505050509295985092959850929598565b6000806040838503121561173157600080fd5b61173a836115c8565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561178757611787611748565b604052919050565b6000604082840312156117a157600080fd5b6040516040810181811067ffffffffffffffff821117156117c4576117c4611748565b604052823581526020928301359281019290925250919050565b600080606083850312156117f157600080fd5b82359150611802846020850161178f565b90509250929050565b6000806000806000806000806000898b0361010081121561182b57600080fd5b8a35995060208b0135985060408b0135975060608b0135965060808b013567ffffffffffffffff8082111561185f57600080fd5b61186b8e838f01611606565b909850965060a08d013591508082111561188457600080fd5b506118918d828e01611606565b909550935050604060bf19820112156118a957600080fd5b5060c08a0190509295985092959850929598565b600080604083850312156118d057600080fd5b823567ffffffffffffffff808211156118e857600080fd5b818501915085601f8301126118fc57600080fd5b813560208282111561191057611910611748565b8160051b925061192181840161175e565b828152928401810192818101908985111561193b57600080fd5b948201945b8486101561196057611951866115c8565b82529482019490820190611940565b9997909101359750505050505050565b60006020828403121561198257600080fd5b5051919050565b60006020828403121561199b57600080fd5b815180151581146115ff57600080fd5b634e487b7160e01b600052601160045260246000fd5b6000828210156119d3576119d36119ab565b500390565b6000604082840312156119ea57600080fd5b6115ff838361178f565b84815283602082015260606040820152816060820152818360808301376000818301608090810191909152601f909201601f191601019392505050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b6000600019821415611a8757611a876119ab565b5060010190565b60008083128015600160ff1b850184121615611aac57611aac6119ab565b6001600160ff1b0384018313811615611ac757611ac76119ab565b5050039056fea26469706673582212203c8579ab57f41734db4c3eda0f669fe9f6b41b98607cbe86b5d2a1a1470d126664736f6c63430008090033' # noqa LIDO_LOCATOR_BYTECODE = '0x6102406040523480156200001257600080fd5b5060405162000a3238038062000a32833981016040819052620000359162000254565b80516200004290620001d1565b6001600160a01b031660805260208101516200005e90620001d1565b6001600160a01b031660a05260408101516200007a90620001d1565b6001600160a01b031660c05260608101516200009690620001d1565b6001600160a01b031660e0526080810151620000b290620001d1565b6001600160a01b03166101005260a0810151620000cf90620001d1565b6001600160a01b03166101205260c0810151620000ec90620001d1565b6001600160a01b03166101405260e08101516200010990620001d1565b6001600160a01b0316610160526101008101516200012790620001d1565b6001600160a01b0316610180526101208101516200014590620001d1565b6001600160a01b03166101a0526101408101516200016390620001d1565b6001600160a01b03166101c0526101608101516200018190620001d1565b6001600160a01b03166101e0526101808101516200019f90620001d1565b6001600160a01b0316610200526101a0810151620001bd90620001d1565b6001600160a01b0316610220525062000383565b60006001600160a01b038216620001fb5760405163d92e233d60e01b815260040160405180910390fd5b5090565b6040516101c081016001600160401b03811182821017156200023157634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200024f57600080fd5b919050565b60006101c082840312156200026857600080fd5b62000272620001ff565b6200027d8362000237565b81526200028d6020840162000237565b6020820152620002a06040840162000237565b6040820152620002b36060840162000237565b6060820152620002c66080840162000237565b6080820152620002d960a0840162000237565b60a0820152620002ec60c0840162000237565b60c0820152620002ff60e0840162000237565b60e08201526101006200031481850162000237565b908201526101206200032884820162000237565b908201526101406200033c84820162000237565b908201526101606200035084820162000237565b908201526101806200036484820162000237565b908201526101a06200037884820162000237565b908201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516105ac6200048660003960006103f501526000818161026c015281816103980152610491015260008181610244015281816102a101526103700152600061010a01526000818161021c015261046a0152600081816101f2015261052d01526000818161017501526103480152600081816103c001526104df0152600081816101ca0152818161031e01526105540152600061014e015260006104b80152600081816101a5015281816102f601526105060152600061041c0152600081816102d1015261044301526105ac6000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80635a2031f911610097578063d680a87611610066578063d680a876146104da578063e441d25f14610501578063ef6c064c14610528578063f5e6d50f1461054f57600080fd5b80635a2031f91461043e57806361d027b31461046557806369d421481461048c578063996107aa146104b357600080fd5b806337d5fe99116100d357806337d5fe991461029c5780633cbf357e146102c35780633fe7d554146103f0578063472c17761461041757600080fd5b806312f8d4b91461010557806323509a2d1461014957806327810b6e1461017057806335f4022e14610197575b600080fd5b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000008116928201929092527f0000000000000000000000000000000000000000000000000000000000000000821660608201527f0000000000000000000000000000000000000000000000000000000000000000821660808201527f00000000000000000000000000000000000000000000000000000000000000009190911660a082015260c001610140565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000008116928201929092527f0000000000000000000000000000000000000000000000000000000000000000821660608201527f0000000000000000000000000000000000000000000000000000000000000000821660808201527f0000000000000000000000000000000000000000000000000000000000000000821660a08201527f00000000000000000000000000000000000000000000000000000000000000009190911660c082015260e001610140565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f000000000000000000000000000000000000000000000000000000000000000081565b61012c7f00000000000000000000000000000000000000000000000000000000000000008156fea2646970667358221220f56a7e9b46d8e22c7fbd5bfb01e236cd03fd8640f62ca4bf7a383de86337258d64736f6c63430008090033' # noqa @@ -135,7 +171,7 @@ def upgrade_staking_router_to_v2(web3_lido_integration: Web3): admin = proxy.functions.proxy__getAdmin().call() - web3_lido_integration.provider.make_request('anvil_setBalance', [admin, 10**18]) + web3_lido_integration.provider.make_request('anvil_setBalance', [admin, 10 ** 18]) tx_hash = proxy.functions.proxy__upgradeTo(new_locator).transact({'from': admin}) web3_lido_integration.eth.wait_for_transaction_receipt(tx_hash) diff --git a/tests/fixtures/strategy.py b/tests/fixtures/strategy.py new file mode 100644 index 00000000..d030983a --- /dev/null +++ b/tests/fixtures/strategy.py @@ -0,0 +1,44 @@ +import pytest +from blockchain.deposit_strategy.base_deposit_strategy import BaseDepositStrategy, MellowDepositStrategy +from blockchain.deposit_strategy.deposit_transaction_sender import Sender +from blockchain.deposit_strategy.gas_price_calculator import GasPriceCalculator + + +@pytest.fixture +def base_deposit_strategy(web3_lido_unit): + yield BaseDepositStrategy(web3_lido_unit) + + +@pytest.fixture +def base_deposit_strategy_integration(web3_lido_integration): + yield BaseDepositStrategy(web3_lido_integration) + + +@pytest.fixture +def mellow_deposit_strategy(web3_lido_unit): + yield MellowDepositStrategy(web3_lido_unit) + + +@pytest.fixture +def mellow_deposit_strategy_integration(web3_lido_integration): + yield MellowDepositStrategy(web3_lido_integration) + + +@pytest.fixture +def deposit_transaction_sender(web3_lido_unit): + yield Sender(web3_lido_unit) + + +@pytest.fixture +def deposit_transaction_sender_integration(web3_lido_integration): + yield Sender(web3_lido_integration) + + +@pytest.fixture +def gas_price_calculator(web3_lido_unit): + yield GasPriceCalculator(web3_lido_unit) + + +@pytest.fixture +def gas_price_calculator_integration(web3_lido_integration): + yield GasPriceCalculator(web3_lido_integration) diff --git a/tests/fork.py b/tests/fork.py index c6824033..decc92a5 100644 --- a/tests/fork.py +++ b/tests/fork.py @@ -35,7 +35,7 @@ def __enter__(self): ], ) # Wait until server is ready - time.sleep(2) + time.sleep(5) return self.process def __exit__(self, exc_type, exc_val, exc_tb):