mirror of
https://github.com/autistic-symposium/web3-starter-py.git
synced 2025-05-17 14:10:21 -04:00
💾
This commit is contained in:
parent
0696b08d04
commit
314bdf533e
94 changed files with 11 additions and 7 deletions
65
web3-toolkit/.env.example
Normal file
65
web3-toolkit/.env.example
Normal file
|
@ -0,0 +1,65 @@
|
|||
##########
|
||||
# general
|
||||
##########
|
||||
|
||||
##### options are: 'error' < 'info' < 'debug'
|
||||
LOG_LEVEL=info
|
||||
|
||||
PROVIDER_TYPE=
|
||||
PROVIDER_URL=
|
||||
BLOCK_NUMBER=
|
||||
|
||||
|
||||
######################
|
||||
# get_block_history.py
|
||||
######################
|
||||
|
||||
#### options are 'local_ipc', 'local_http', 'local_ws', 'ipc', 'ws', 'http'
|
||||
#### for locally run nodes, an IPC connection is the mosts secure option.
|
||||
|
||||
|
||||
PAIR_ADDRESSES=
|
||||
ABI_JSON_PATH=
|
||||
|
||||
|
||||
##############################
|
||||
# get_deep_block_data.py
|
||||
#############################
|
||||
|
||||
|
||||
|
||||
##############################
|
||||
# get_deep_tx_data.py
|
||||
#############################
|
||||
|
||||
TRANSACTION=
|
||||
|
||||
|
||||
##############################
|
||||
# get_contracts_deployed.py
|
||||
#############################
|
||||
|
||||
START_BLOCK=
|
||||
TX_FILE=
|
||||
|
||||
|
||||
##############################
|
||||
# decode_calldata.py
|
||||
#############################
|
||||
|
||||
|
||||
CONTRACT_ADDRESS=
|
||||
ABI=
|
||||
CALLDATA=
|
||||
|
||||
|
||||
############################################################
|
||||
# get_transfer_logs_and_wallets_balance_for_a_token.py
|
||||
###########################################################
|
||||
|
||||
RPC_PROVIDER_URL = https://mainnet.infura.io/v3/<your infura project id>
|
||||
CHUNK_SIZE = 100000
|
||||
NUM_ATTEMPTS = 3
|
||||
TRANSFER_EVENT_TOPIC_HASH = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
|
||||
TOKEN_ADDRESS =
|
||||
DECIMALS =
|
12
web3-toolkit/Makefile
Normal file
12
web3-toolkit/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
.PHONY: clean install lint
|
||||
|
||||
install:
|
||||
poetry install
|
||||
|
||||
clean:
|
||||
@find . -iname '__pycache__' -delete
|
||||
poetry cache clear
|
||||
poetry env remove --all
|
||||
|
||||
lint:
|
||||
tox -e lint
|
148
web3-toolkit/README.md
Normal file
148
web3-toolkit/README.md
Normal file
|
@ -0,0 +1,148 @@
|
|||
## 📚 web3-python-toolkit
|
||||
|
||||
<br>
|
||||
|
||||
#### 👉 *on-going* development of a library and set of python scripts with my fav on-chain ops.
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
### setting up
|
||||
|
||||
we use [poetry](https://python-poetry.org/docs/) to setup this package (config details at `pyproject.toml`).
|
||||
|
||||
installing:
|
||||
|
||||
```
|
||||
brew install poetry
|
||||
make install
|
||||
cp .env.example .env
|
||||
vim .env
|
||||
```
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
----
|
||||
|
||||
### scripts
|
||||
|
||||
<br>
|
||||
|
||||
#### get contracts deployed to mainnet and testnets
|
||||
|
||||
we leverage [ethereumetl](https://github.com/blockchain-etl/ethereum-etl) to extract this data.
|
||||
|
||||
1. add info to `.env`
|
||||
2. run
|
||||
```
|
||||
cd scripts
|
||||
poetry run python get_contracts_deployed.py
|
||||
```
|
||||
3. any output is saved to `data/`.
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
#### get reserve history by block for a pair of addresses
|
||||
|
||||
1. add the pair abi to `abis/`
|
||||
2. run
|
||||
```
|
||||
cd scripts
|
||||
poetry run python get_reserve_history_by_block.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
#### get deep block data
|
||||
|
||||
1. add info to `.env`
|
||||
2. run
|
||||
```
|
||||
cd scripts
|
||||
poetry run python get_deep_block_data.py
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
#### decode calldata
|
||||
|
||||
1. add info to `.env`
|
||||
2. run
|
||||
```
|
||||
cd scripts
|
||||
poetry run python decode_calldata.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### get token transfer events logs and wallet balances
|
||||
|
||||
1. add info to `.env`
|
||||
2. run
|
||||
```
|
||||
cd scripts
|
||||
poetry run python get_transfer_logs_and_wallets_balance_for_a_token.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
### troubleshoot
|
||||
|
||||
<br>
|
||||
|
||||
##### if you see `ethereum-etl not compatible to m1` run:
|
||||
|
||||
```
|
||||
pip uninstall ethereum-etl
|
||||
pip install --no-binary ethereum-etl
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
### resources
|
||||
|
||||
|
||||
* [web3.py library](https://web3py.readthedocs.io/en/v5/)
|
||||
* [ethereum etl library](https://ethereum-etl.readthedocs.io/en/latest/quickstart/)
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
### relevant info
|
||||
|
||||
<br>
|
||||
|
||||
##### providers
|
||||
|
||||
- providers are how libraries such as `web3.py` talk to the blockchain.
|
||||
- providers take `JSON-RPC` requests and return responses
|
||||
- the most common ways to connect to your node are:
|
||||
- IPC (uses local filesystem, fastest and most secure)
|
||||
- Websockets (works remotely, faster than HTTP)
|
||||
- HTTP (more nodes support it)
|
||||
|
||||
<br>
|
||||
|
||||
##### middleware
|
||||
|
||||
* a web3.py instance can be configured via middleware (sitting between the web3 methods and the provider).
|
||||
* middlewares use an onion metaphor: each layer may affect both the request and response from the provider.
|
||||
* each middleware layer gets invoked before the request reaches the provider, and then processes the result after the provider returns, in reverse order.
|
||||
* we often use `geth_poa_middleware`, to run with geth's Proof-of-Authority (PoA) consensus. this adds support for more than 32 bytes in each block (the `extraData` field).
|
658
web3-toolkit/abis/LP_pair.json
Normal file
658
web3-toolkit/abis/LP_pair.json
Normal file
|
@ -0,0 +1,658 @@
|
|||
[
|
||||
{
|
||||
"inputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Approval",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "amount0",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "amount1",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "Burn",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "amount0",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "amount1",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Mint",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "amount0In",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "amount1In",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "amount0Out",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "amount1Out",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "Swap",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint112",
|
||||
"name": "reserve0",
|
||||
"type": "uint112"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint112",
|
||||
"name": "reserve1",
|
||||
"type": "uint112"
|
||||
}
|
||||
],
|
||||
"name": "Sync",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Transfer",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "DOMAIN_SEPARATOR",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "MINIMUM_LIQUIDITY",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "PERMIT_TYPEHASH",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "allowance",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "approve",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "burn",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount0",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount1",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint8",
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "factory",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getReserves",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint112",
|
||||
"name": "_reserve0",
|
||||
"type": "uint112"
|
||||
},
|
||||
{
|
||||
"internalType": "uint112",
|
||||
"name": "_reserve1",
|
||||
"type": "uint112"
|
||||
},
|
||||
{
|
||||
"internalType": "uint32",
|
||||
"name": "_blockTimestampLast",
|
||||
"type": "uint32"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_token0",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_token1",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "initialize",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "kLast",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "mint",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "liquidity",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "name",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "nonces",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint8",
|
||||
"name": "v",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "r",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "s",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "permit",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "price0CumulativeLast",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "price1CumulativeLast",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "skim",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount0Out",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount1Out",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "swap",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "symbol",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "sync",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "token0",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "token1",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "totalSupply",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transferFrom",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
1728
web3-toolkit/poetry.lock
generated
Normal file
1728
web3-toolkit/poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
21
web3-toolkit/pyproject.toml
Normal file
21
web3-toolkit/pyproject.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[tool.poetry]
|
||||
name = "web3-python-toolkit"
|
||||
version = "0.1.0"
|
||||
description="toolkit for web3"
|
||||
authors=["steinkirch"]
|
||||
readme = "README.md"
|
||||
packages = [{include = "scripts"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "3.11.1"
|
||||
ethereum-etl = "2.1.2"
|
||||
python-dotenv = "0.21.0"
|
||||
web3 = "5.31.3"
|
||||
pandas = "^1.5.2"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
tox = "^4.0.16"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
1
web3-toolkit/scripts/__init__.py
Normal file
1
web3-toolkit/scripts/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
# -*- encoding: utf-8 -*-
|
43
web3-toolkit/scripts/decode_calldata.py
Normal file
43
web3-toolkit/scripts/decode_calldata.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
# author: steinkirch
|
||||
|
||||
from utils.strings import pprint
|
||||
from utils.os import load_config
|
||||
from utils.web3_wrapper import Web3Wrapper
|
||||
|
||||
|
||||
def get_data_for_connection() -> dict:
|
||||
"""Prepare a dict of data for connection."""
|
||||
|
||||
data = {}
|
||||
env_keys = ['PROVIDER_URL',
|
||||
'PROVIDER_TYPE',
|
||||
'CONTRACT_ADDRESS',
|
||||
'ABI',
|
||||
'CALLDATA']
|
||||
env_vars = load_config(env_keys)
|
||||
|
||||
data['network'] = env_vars['PROVIDER_URL']
|
||||
data['provider_type'] = env_vars['PROVIDER_URL']
|
||||
data['contract_address'] = env_vars['CONTRACT_ADDRESS']
|
||||
data['calldata'] = env_vars['CALLDATA']
|
||||
data['abi'] = env_vars['ABI']
|
||||
return data
|
||||
|
||||
|
||||
def decode_calldata(data) -> dict:
|
||||
"""Decode raw calldata, given a contract and its ABI."""
|
||||
|
||||
w3 = Web3Wrapper(mode=data['provider_type'],
|
||||
network=data['network'])
|
||||
|
||||
w3.get_pair_contract(address=data['contract_address'], abi=data['abi'])
|
||||
return w3.pair_contract.decode_function_input(data['calldata'])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
data = get_data_for_connection()
|
||||
results = decode_calldata(data)
|
||||
pprint(results)
|
113
web3-toolkit/scripts/get_contracts_deployed.py
Executable file
113
web3-toolkit/scripts/get_contracts_deployed.py
Executable file
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
# author: steinkirch
|
||||
|
||||
import os
|
||||
import ethereumetl
|
||||
import pandas as pd
|
||||
from utils.os import load_config, create_dir, run_exec
|
||||
from utils.plots import plot_bar, open_csv, save_csv
|
||||
|
||||
|
||||
|
||||
def get_data_for_contracts_by_block() -> dict:
|
||||
"""Prepare a dict of data to extract contracts by block."""
|
||||
|
||||
data = {}
|
||||
create_dir('data')
|
||||
env_keys = ['PROVIDER_URL',
|
||||
'TX_FILE',
|
||||
'START_BLOCK',
|
||||
'OUTPUT_FILE']
|
||||
env_vars = load_config(env_keys)
|
||||
|
||||
data['provider_uri'] = env_vars['PROVIDER_URL']
|
||||
data['tx_file'] = env_vars['TX_FILE']
|
||||
data['start_block'] = env_vars['START_BLOCK']
|
||||
data['output_file'] = env_vars['OUTPUT_FILE']
|
||||
|
||||
# adding this manually
|
||||
data['last_block_2015'] = 778482
|
||||
data['last_block_2016'] = 2912406
|
||||
data['last_block_2017'] = 4832685
|
||||
data['last_block_2018'] = 6988614
|
||||
data['last_block_2019'] = 9193265
|
||||
data['last_block_2020'] = 11565018
|
||||
data['last_block_2021'] = 13916165
|
||||
data['last_block_2022'] = 15978869
|
||||
data['buffer_for_chunk_size'] = 10000
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def export_blocks_and_transactions(end_block, data) -> dict:
|
||||
"""Run ethereumetl export_blocks_and_transactions."""
|
||||
|
||||
run_exec(['ethereumetl', 'export_blocks_and_transactions', \
|
||||
f'--start-block {data["start_block"]}', \
|
||||
f'--end-block {end_block}', \
|
||||
f'--provider-uri {data["provider_uri"]}', \
|
||||
f'--transactions-output {data["tx_file"]}'])
|
||||
|
||||
txs = open_csv(data['tx_file'])
|
||||
contracts = txs[txs['to_address'].isnull()]
|
||||
os.remove(data['tx_file'])
|
||||
return contracts + txs['from_address'].unique().tolist()
|
||||
|
||||
|
||||
def get_contracts_by_block(data, year) -> dict:
|
||||
"""Extract unique contracts by block for a given year."""
|
||||
|
||||
contracts = []
|
||||
last_block_year = data[f'last_block_{year}']
|
||||
start_block = int(data['start_block'])
|
||||
end_block = start_block + 9999
|
||||
|
||||
while (end_block <= last_block_year + data['buffer_for_chunk_size']):
|
||||
end_block_used = min(end_block, last_block_year)
|
||||
contracts.append(export_blocks_and_transactions(end_block_used, data))
|
||||
start_block += data['buffer_for_chunk_size']
|
||||
end_block += data['buffer_for_chunk_size']
|
||||
|
||||
return contracts
|
||||
|
||||
|
||||
def get_unique_contracts(contracts, year) -> None:
|
||||
"""Extract and save a list of unique contracts."""
|
||||
|
||||
unique_contract = [*set(contracts)]
|
||||
print(f"✅ Unique contract for {year})): {str(len(unique_contract))}")
|
||||
return pd.DataFrame(unique_contract, columns=["contracts"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
###########
|
||||
# Set up
|
||||
###########
|
||||
contracts_by_year = {}
|
||||
all_contracts = pd.DataFrame()
|
||||
years = list(range(2015, 2022))
|
||||
data = get_data_for_contracts_by_block()
|
||||
start_block = int(data['start_block'])
|
||||
|
||||
###########
|
||||
# Get data
|
||||
###########
|
||||
for year in years:
|
||||
contracts = get_contracts_by_block(data, year)
|
||||
contracts_by_year[year] = len(contracts)
|
||||
all_contracts.append(get_unique_contracts(contracts, year))
|
||||
start_block += 1
|
||||
|
||||
all_contracts = all_contracts['contract_deployers'].unique()
|
||||
all_contracts_df = pd.DataFrame(all_contracts, columns=['contract'])
|
||||
save_csv(all_contracts_df, data['output_file'])
|
||||
|
||||
###########
|
||||
# Plot data
|
||||
###########
|
||||
y_data = [y for y in contracts_by_year[year] if year == years.revers().pop()]
|
||||
plot_bar({'contract deployed': y_data}, years)
|
||||
|
||||
|
37
web3-toolkit/scripts/get_deep_block_data.py
Executable file
37
web3-toolkit/scripts/get_deep_block_data.py
Executable file
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
# author: steinkirch
|
||||
|
||||
from utils.strings import pprint
|
||||
from utils.os import load_config
|
||||
from utils.web3_wrapper import Web3Wrapper
|
||||
|
||||
|
||||
def get_data_for_connection() -> dict:
|
||||
"""Prepare a dict of data for connection."""
|
||||
|
||||
data = {}
|
||||
env_keys = ['PROVIDER_TYPE',
|
||||
'PROVIDER_URL',
|
||||
'BLOCK_NUMBER']
|
||||
env_vars = load_config(env_keys)
|
||||
|
||||
data['network'] = env_vars['PROVIDER_URL']
|
||||
data['block'] = env_vars['BLOCK_NUMBER']
|
||||
data['provider_type'] = env_vars['PROVIDER_TYPE']
|
||||
return data
|
||||
|
||||
|
||||
def get_deep_block_data(data) -> dict:
|
||||
|
||||
w3 = Web3Wrapper(mode=data['provider_type'],
|
||||
network=data['network'])
|
||||
return w3.get_block()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
data = get_data_for_connection()
|
||||
results = get_deep_block_data(data)
|
||||
pprint(results)
|
||||
|
40
web3-toolkit/scripts/get_deep_tx_data.py
Executable file
40
web3-toolkit/scripts/get_deep_tx_data.py
Executable file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
# author: steinkirch
|
||||
|
||||
from utils.strings import pprint
|
||||
from utils.os import load_config
|
||||
from utils.web3_wrapper import Web3Wrapper
|
||||
|
||||
|
||||
def get_data_for_connection() -> dict:
|
||||
"""Prepare a dict of data for connection."""
|
||||
|
||||
data = {}
|
||||
env_keys = ['PROVIDER_TYPE',
|
||||
'PROVIDER_URL',
|
||||
'TRANSACTION']
|
||||
env_vars = load_config(env_keys)
|
||||
|
||||
data['network'] = env_vars['PROVIDER_URL']
|
||||
data['provider_type'] = env_vars['PROVIDER_TYPE']
|
||||
data['tx'] = env_vars['TRANSACTION']
|
||||
return data
|
||||
|
||||
|
||||
def get_deep_tx_data(data) -> dict:
|
||||
|
||||
w3 = Web3Wrapper(mode=data['provider_type'],
|
||||
network=data['network'])
|
||||
tx_data = w3.get_tx(data['tx'])
|
||||
tx_data.update(w3.get_tx_receipt(data['tx']))
|
||||
|
||||
return tx_data
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
data = get_data_for_connection()
|
||||
tx_data = get_deep_tx_data(data)
|
||||
pprint(tx_data)
|
||||
|
42
web3-toolkit/scripts/get_reserve_history_by_block.py
Executable file
42
web3-toolkit/scripts/get_reserve_history_by_block.py
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
# author: steinkirch
|
||||
|
||||
from utils.os import load_config, open_json, log_info
|
||||
from utils.web3_wrapper import Web3Wrapper
|
||||
|
||||
|
||||
def get_data_for_connection() -> dict:
|
||||
"""Prepare a dict of data for connection."""
|
||||
|
||||
data = {}
|
||||
env_keys = ['PAIR_ADDRESSES',
|
||||
'PROVIDER_URL',
|
||||
'BLOCK_NUMBER',
|
||||
'ABI_JSON_PATH',
|
||||
'PROVIDER_TYPE']
|
||||
env_vars = load_config(env_keys)
|
||||
|
||||
data['addresses'] = env_vars['PAIR_ADDRESSES']
|
||||
data['network'] = env_vars['PROVIDER_URL']
|
||||
data['block'] = env_vars['BLOCK_NUMBER']
|
||||
data['abi'] = env_vars['ABI_JSON_PATH']
|
||||
data['provider_type'] = env_vars['PROVIDER_TYPE']
|
||||
return data
|
||||
|
||||
|
||||
def get_reserve_by_block(data) -> None:
|
||||
"""Establish connection to retrieve reserve history."""
|
||||
|
||||
w3 = Web3Wrapper(mode=data['provider_type'],
|
||||
network=data['network'])
|
||||
w3.inject_middleware()
|
||||
w3.get_pair_contract(data['addresses'], open_json(data['abi']))
|
||||
return w3.get_reserves(int(data['block']))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
data = get_data_for_connection()
|
||||
reserve1, reserve2 = get_reserve_by_block(data)
|
||||
log_info(f'reserves: {reserve1}, {reserve2}')
|
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
# author: steinkirch
|
||||
#
|
||||
############################################################################################
|
||||
#
|
||||
# this script is used to get transfer logs through infura's api and then parse
|
||||
# these logs to calculate the balance of a wallet for a given token.
|
||||
#
|
||||
# to run, create an .env file with the following variables:
|
||||
# RPC_PROVIDER_URL = https://mainnet.infura.io/v3/<your infura project id>
|
||||
# CHUNK_SIZE = 100000
|
||||
# NUM_ATTEMPTS = 3
|
||||
# TRANSFER_EVENT_TOPIC_HASH = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
|
||||
# TOKEN_ADDRESS =
|
||||
# DECIMALS =
|
||||
#
|
||||
############################################################################################
|
||||
|
||||
|
||||
import os
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from decimal import Decimal
|
||||
from dotenv import load_dotenv
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def get_env():
|
||||
"""Load environment variables from .env file"""
|
||||
|
||||
load_dotenv()
|
||||
env_path = Path('.')/'.env'
|
||||
load_dotenv(dotenv_path=env_path)
|
||||
|
||||
env_data = {}
|
||||
env_data['RPC_PROVIDER_URL'] = os.getenv("RPC_PROVIDER_URL")
|
||||
env_data['CHUNK_SIZE'] = os.getenv("CHUNK_SIZE")
|
||||
env_data['NUM_ATTEMPTS'] = os.getenv("NUM_ATTEMPTS")
|
||||
env_data['TRANSFER_EVENT_TOPIC_HASH'] = os.getenv("TRANSFER_EVENT_TOPIC_HASH")
|
||||
env_data['TOKEN_ADDRESS'] = os.getenv("TOKEN_ADDRESS")
|
||||
env_data['DECIMALS'] = os.getenv("DECIMALS")
|
||||
|
||||
if not (bool(env_data['RPC_PROVIDER_URL']) or bool(env_data['CHUNK_SIZE']) or \
|
||||
bool(env_data['NUM_ATTEMPTS']) or bool(env_data['TRANSFER_EVENT_TOPIC_HASH']) or \
|
||||
bool(env_data['TOKEN_ADDRESS']) or bool(env_data['DECIMALS'])):
|
||||
raise Exception('Please add config to .env file')
|
||||
|
||||
return env_data
|
||||
|
||||
|
||||
def convert_hex_to_int(hex_string: str) -> int:
|
||||
"""Convert a hex string to an integer"""
|
||||
|
||||
return int(hex_string, 16)
|
||||
|
||||
|
||||
def send_rpc_request(url, method, params=None) -> dict:
|
||||
"""Send a JSON-RPC request to a given URL"""
|
||||
|
||||
params = params or []
|
||||
data = {'jsonrpc': '2.0', 'method': method, 'params': params, 'id': 1}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers={'Content-Type': 'application/json'}, json=data)
|
||||
if response.status_code == 200:
|
||||
return response.json()['result']
|
||||
else:
|
||||
print('Query failed: {}.'.format(response.status_code))
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print('Error querying to {0}: {1}'.format(url, e.response.text))
|
||||
|
||||
except KeyError:
|
||||
print('Error querying to {0}: data not valid'.format(url))
|
||||
|
||||
|
||||
def get_logs(address: str, from_block: int, to_block: int, topic: str, url: str) -> list:
|
||||
"""Get logs from a given address between two blocks"""
|
||||
|
||||
# https://docs.infura.io/infura/networks/ethereum/json-rpc-methods/eth_getlogs
|
||||
method = 'eth_getLogs'
|
||||
print(f'loading blocks {from_block} to {to_block}')
|
||||
|
||||
return send_rpc_request(url, method,
|
||||
[{'address': address,
|
||||
'fromBlock': from_block,
|
||||
'toBlock': to_block,
|
||||
'topics': [topic]
|
||||
}])
|
||||
|
||||
|
||||
def get_last_block_number(url: str) -> int:
|
||||
"""Get the last block number"""
|
||||
|
||||
# https://docs.infura.io/infura/networks/ethereum/json-rpc-methods/eth_blocknumber
|
||||
method = 'eth_blockNumber'
|
||||
return convert_hex_to_int(send_rpc_request(url, method))
|
||||
|
||||
|
||||
def get_transfer_logs(env_data: dict, address: str, decimals: int,
|
||||
from_block=None, to_block=None, skip_chunks=False) -> list:
|
||||
"""Get transfer logs from a given address between two blocks"""
|
||||
|
||||
from_block = from_block or 1
|
||||
to_block = to_block or get_last_block_number(url)
|
||||
topic = env_data['TRANSFER_EVENT_TOPIC_HASH']
|
||||
url = env_data['RPC_PROVIDER_URL']
|
||||
|
||||
#################################
|
||||
# retrieve event logs by chunks
|
||||
#################################
|
||||
if not skip_chunks:
|
||||
|
||||
logs = []
|
||||
c_size = int(env_data['CHUNK_SIZE'])
|
||||
attempts = int(env_data['NUM_ATTEMPTS'])
|
||||
|
||||
for block in range(from_block, to_block, c_size):
|
||||
attempt = 0
|
||||
while attempt < attempts:
|
||||
try:
|
||||
logs += get_logs(address, hex(block), hex(block + c_size), topic, url)
|
||||
break
|
||||
except Exception:
|
||||
attempt += 1
|
||||
|
||||
#################################
|
||||
# retrieve event logs in one go
|
||||
#################################
|
||||
else:
|
||||
logs = get_logs(address, hex(from_block), hex(to_block), topic, url)
|
||||
|
||||
return logs
|
||||
|
||||
|
||||
def ged_processed_logs(logs: list, decimals: int) -> list:
|
||||
"""Process logs to get from, to and amount"""
|
||||
|
||||
decimal = Decimal('10') ** Decimal(f'-{decimals}')
|
||||
processed_logs = defaultdict()
|
||||
|
||||
try:
|
||||
for log in logs:
|
||||
processed_logs[log['transactionHash']] = {}
|
||||
processed_logs[log['transactionHash']]['blockNumber'] = log['blockNumber']
|
||||
processed_logs[log['transactionHash']]['from'] = '0x' + log['topics'][1][26:]
|
||||
processed_logs[log['transactionHash']]['to'] = '0x' + log['topics'][2][26:]
|
||||
processed_logs[log['transactionHash']]['amount'] = Decimal(convert_hex_to_int(log['data'])) * decimal
|
||||
except KeyError as e:
|
||||
print(f'Error processing logs: {e}')
|
||||
|
||||
return processed_logs
|
||||
|
||||
|
||||
def get_balances(transfers: list) -> list:
|
||||
"""Get balances of all addresses that have received tokens"""
|
||||
|
||||
balances = defaultdict(Decimal)
|
||||
|
||||
for _, transfer_data in transfers.items():
|
||||
balances[transfer_data['from']] -= transfer_data['amount']
|
||||
balances[transfer_data['to']] += transfer_data['amount']
|
||||
|
||||
balances = [{'address': k, 'amount': v} for k, v in balances.items() if v > Decimal('0')]
|
||||
return sorted(balances, key=lambda x: -abs(Decimal(x['amount'])))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
env_data = get_env()
|
||||
|
||||
address = env_data['TOKEN_ADDRESS']
|
||||
decimals = env_data['DECIMALS']
|
||||
|
||||
transfer_logs = get_transfer_logs(env_data, address, decimals, from_block=16801268, to_block=16807268, skip_chunks=True)
|
||||
processed_logs = ged_processed_logs(transfer_logs, decimals)
|
||||
balances = get_balances(processed_logs)
|
||||
|
||||
for balance in balances:
|
||||
print(f'{balance["address"]} has {balance["amount"]} tokens')
|
1
web3-toolkit/scripts/utils/__init__.py
Normal file
1
web3-toolkit/scripts/utils/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
# -*- encoding: utf-8 -*-
|
25
web3-toolkit/scripts/utils/arithmetics.py
Normal file
25
web3-toolkit/scripts/utils/arithmetics.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# arithmetics.py
|
||||
# This class implements math methods used by the other classes.
|
||||
# author: steinkirch
|
||||
|
||||
|
||||
from decimal import Decimal, getcontext
|
||||
from utils.strings import log_error
|
||||
|
||||
|
||||
def div(dividend, divisor) -> Decimal:
|
||||
"""Return higher precision division."""
|
||||
|
||||
if divisor == 0:
|
||||
log_error('Found a zero division error. Returning 0.')
|
||||
return 0
|
||||
return to_decimal(dividend) / to_decimal(divisor)
|
||||
|
||||
|
||||
def to_decimal(value, precision=None) -> Decimal:
|
||||
"""Return Decimal value for higher (defined) precision."""
|
||||
|
||||
precision = precision or 22
|
||||
getcontext().prec = precision
|
||||
return Decimal(value)
|
149
web3-toolkit/scripts/utils/os.py
Normal file
149
web3-toolkit/scripts/utils/os.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# This class implements OS/file system util methods used by the other classes.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import copy
|
||||
import logging
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
def set_logging(log_level) -> None:
|
||||
"""Set logging level according to .env config."""
|
||||
|
||||
if log_level == 'info':
|
||||
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
||||
|
||||
elif log_level == 'error':
|
||||
logging.basicConfig(level=logging.ERROR, format='%(message)s')
|
||||
|
||||
elif log_level == 'debug':
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(message)s')
|
||||
|
||||
else:
|
||||
print(f'Logging level {log_level} is not available. Setting to ERROR')
|
||||
logging.basicConfig(level=logging.ERROR, format='%(message)s')
|
||||
|
||||
|
||||
def load_config(keys) -> dict:
|
||||
"""Load and set environment variables."""
|
||||
|
||||
env_file = Path('..') / '.env'
|
||||
if not os.path.isfile(env_file):
|
||||
exit_with_error('Please create an .env file')
|
||||
|
||||
load_dotenv(env_file)
|
||||
env_vars = {}
|
||||
|
||||
try:
|
||||
for key in keys:
|
||||
env_vars[key] = os.getenv(key)
|
||||
set_logging(os.getenv("LOG_LEVEL"))
|
||||
return env_vars
|
||||
except KeyError as e:
|
||||
exit_with_error(f'Cannot extract env variables: {e}. Exiting.')
|
||||
|
||||
|
||||
def log_error(string) -> None:
|
||||
"""Print STDOUT error using the logging library."""
|
||||
|
||||
logging.error('🚨 %s', string)
|
||||
|
||||
|
||||
def log_info(string) -> None:
|
||||
"""Print STDOUT info using the logging library."""
|
||||
|
||||
logging.info('✅ %s', string)
|
||||
|
||||
|
||||
def log_debug(string) -> None:
|
||||
"""Print STDOUT debug using the logging library."""
|
||||
|
||||
logging.debug('🟨 %s', string)
|
||||
|
||||
|
||||
def open_json(filepath) -> dict:
|
||||
"""Load and parse a json file."""
|
||||
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as infile:
|
||||
return json.load(infile)
|
||||
|
||||
except (IOError, FileNotFoundError, TypeError) as e:
|
||||
exit_with_error(f'Failed to parse: "{filepath}": {e}')
|
||||
|
||||
|
||||
def format_path(dir_path, filename) -> str:
|
||||
"""Format a OS full filepath."""
|
||||
|
||||
return os.path.join(dir_path, filename)
|
||||
|
||||
|
||||
def format_output_file(name) -> str:
|
||||
"""Format the name for the result file."""
|
||||
|
||||
return f'{name}.json'
|
||||
|
||||
|
||||
def save_json(destination, data) -> None:
|
||||
"""Save data from memory to a json destination in disk."""
|
||||
|
||||
try:
|
||||
with open(destination, 'w', encoding='utf-8') as outfile:
|
||||
json.dump(data, outfile, indent=4)
|
||||
|
||||
except (IOError, TypeError) as e:
|
||||
log_error(f'Could not save {destination}: {e}')
|
||||
|
||||
|
||||
def create_dir(result_dir) -> None:
|
||||
"""Check whether a directory exists and create it if needed."""
|
||||
|
||||
try:
|
||||
if not os.path.isdir(result_dir):
|
||||
os.mkdir(result_dir)
|
||||
|
||||
except OSError as e:
|
||||
log_error(f'Could not create {result_dir}: {e}')
|
||||
|
||||
|
||||
def set_output(env_vars, input_file) -> str:
|
||||
"""Create an output destination to save solutions."""
|
||||
|
||||
try:
|
||||
output_dir = env_vars['OUTPUT_DIR']
|
||||
create_dir(output_dir)
|
||||
|
||||
output_str = input_file.split('_')[1].split('.json')[0]
|
||||
output_file_str = env_vars['OUTPUT_FILE_STR']
|
||||
output_file = output_file_str.format(output_str)
|
||||
return format_path(output_dir, output_file)
|
||||
|
||||
except (TypeError, KeyError) as e:
|
||||
exit_with_error(f'Could not format output file: {e}')
|
||||
|
||||
|
||||
def deep_copy(dict_to_clone) -> dict:
|
||||
"""Deep copy (not reference copy) to a dict."""
|
||||
|
||||
return copy.deepcopy(dict_to_clone)
|
||||
|
||||
|
||||
def exit_with_error(message) -> None:
|
||||
"""Log an error message and halt the program."""
|
||||
|
||||
log_error(message)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def run_exec(command) -> None:
|
||||
"""Exec a bash commnad (remember: not safe!)."""
|
||||
|
||||
try:
|
||||
this_command = subprocess.run(command)
|
||||
log_info(f'Exit code: {this_command.returncode}')
|
||||
except Exception as e:
|
||||
log_error(f'Error running {command}: {e}')
|
32
web3-toolkit/scripts/utils/plots.py
Normal file
32
web3-toolkit/scripts/utils/plots.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# This class implements plot scripts
|
||||
# author: steinkirch
|
||||
|
||||
import pandas as pd
|
||||
from utils.os import exit_with_error, log_error
|
||||
|
||||
|
||||
def open_csv(filepath) -> dict:
|
||||
"""Load and parse a csv file."""
|
||||
|
||||
try:
|
||||
return pd.read_csv(filepath)
|
||||
except (IOError, FileNotFoundError, TypeError) as e:
|
||||
exit_with_error(f'Failed to parse: "{filepath}": {e}')
|
||||
|
||||
|
||||
def save_csv(destination, data, index=False) -> None:
|
||||
"""Save data from memory to a csv destination in disk."""
|
||||
|
||||
try:
|
||||
data.to_csv(destination, index=index)
|
||||
|
||||
except (IOError, TypeError) as e:
|
||||
log_error(f'Could not save {destination}: {e}')
|
||||
|
||||
|
||||
def plot_bar(y, x) -> None:
|
||||
"""Simplest plot for two sets."""
|
||||
|
||||
df = pd.DataFrame(y, index=x)
|
||||
df.plot.bar(rot=0, subplots=True)
|
40
web3-toolkit/scripts/utils/strings.py
Normal file
40
web3-toolkit/scripts/utils/strings.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# This class implements string methods used by the other classes.
|
||||
# author: steinkirch
|
||||
|
||||
from pprint import PrettyPrinter
|
||||
|
||||
from utils.os import log_error
|
||||
from utils.arithmetics import to_decimal
|
||||
|
||||
|
||||
def to_decimal_str(value) -> str:
|
||||
"""Format a reserve amount to a suitable string."""
|
||||
|
||||
return str(to_decimal(value))
|
||||
|
||||
|
||||
def to_wei_str(value, decimals=None) -> str:
|
||||
"""Parse an order string to wei value."""
|
||||
|
||||
decimals = decimals or 18
|
||||
try:
|
||||
return str(value)[:-decimals] + '_' + str(value)[-decimals:]
|
||||
except ValueError as e:
|
||||
log_error(f'Cannot convert to wei: {e}')
|
||||
|
||||
|
||||
def to_solution(value) -> str:
|
||||
"""Format decimal wei with an underscore for easier reading."""
|
||||
|
||||
return to_wei_str(to_decimal_str(value))
|
||||
|
||||
|
||||
def pprint(data, indent=None) -> None:
|
||||
"""Print dicts and data in a suitable format"""
|
||||
|
||||
print()
|
||||
indent = indent or 4
|
||||
pp = PrettyPrinter(indent=indent)
|
||||
pp.pprint(data)
|
||||
print()
|
67
web3-toolkit/scripts/utils/web3_wrapper.py
Normal file
67
web3-toolkit/scripts/utils/web3_wrapper.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
# This class implements an (ongoing) wrapper for web3 libs.
|
||||
# author: steinkirch
|
||||
|
||||
from web3 import Web3, HTTPProvider, WebsocketProvider, IPCProvider
|
||||
from web3.middleware import geth_poa_middleware
|
||||
from utils.os import log_info
|
||||
|
||||
|
||||
class Web3Wrapper():
|
||||
|
||||
def __init__(self, mode, network):
|
||||
self.mode = mode
|
||||
self.network = network
|
||||
|
||||
self.w3 = None
|
||||
self.pair_contract = None
|
||||
|
||||
self._setup()
|
||||
|
||||
##################
|
||||
# PRIVATE METHODS
|
||||
##################
|
||||
def _setup(self) -> None:
|
||||
self._get_web3_object()
|
||||
|
||||
def _get_web3_object(self) -> None:
|
||||
log_info(f'Setting mode {self.mode} for {self.network}')
|
||||
if self.mode == 'http' or self.mode == 'local_http':
|
||||
self.w3 = Web3(HTTPProvider(self.network))
|
||||
elif self.mode == 'ws' or self.mode == 'local_ws':
|
||||
self.w3 = Web3(WebsocketProvider(self.network))
|
||||
elif self.mode == 'ipc' or self.mode == 'local_ipc':
|
||||
self.w3 = Web3(IPCProvider(self.network))
|
||||
else:
|
||||
log_info(f'Provider type is invalid: {self.mode}. Fix .env.')
|
||||
|
||||
|
||||
##################
|
||||
# BLOCK METHODS
|
||||
##################
|
||||
def get_block(self, block_number='latest') -> dict:
|
||||
return dict(self.w3.eth.get_block(block_number))
|
||||
|
||||
|
||||
##################
|
||||
# LP PAIR METHODS
|
||||
##################
|
||||
def get_pair_contract(self, address, abi) -> str:
|
||||
self.pair_contract = self.w3.eth.contract(address=address, abi=abi)
|
||||
|
||||
def inject_middleware(self, layer=0) -> None:
|
||||
self.w3.middleware_onion.inject(geth_poa_middleware,
|
||||
layer=layer)
|
||||
|
||||
def get_reserves(self, block) -> list:
|
||||
return self.pair_contract.functions.getReserves().call({}, block)[:2]
|
||||
|
||||
|
||||
##############
|
||||
# TX METHODS
|
||||
##############
|
||||
def get_tx(self, tx) -> dict:
|
||||
return dict(self.w3.eth.get_transaction(tx))
|
||||
|
||||
def get_tx_receipt(self, tx) -> dict:
|
||||
return dict(self.w3.eth.get_transaction_receipt(tx))
|
Loading…
Add table
Add a link
Reference in a new issue