├── tests
├── __init__.py
├── accounts
│ ├── pass
│ ├── 0_0x9596c16d7bf9323265c2f2e22f43e6c80eb3d943.json
│ ├── 1_0xe415482ca06eeb684ad3f758c2129fca4b1eb1f4.json
│ ├── 2_0x270b0e8d873e858abd698a000b0da0b94e21d84c.json
│ ├── 3_0x812e87be5d4198fca55cb52fa60cb46620617474.json
│ ├── 4_0x13314e21cd6d343ceb857073f3f6d9368919d1ef.json
│ └── 5_0x176087fea5c41fc370fabbd850521bc4451690ca.json
├── abi
│ ├── OasisMockPriceOracle.sol
│ ├── OasisMockPriceOracle.abi
│ ├── GemMock.sol
│ ├── OasisMockPriceOracle.bin
│ ├── DaiMock.abi
│ └── DaiMock.sol
├── config
│ └── keys
│ │ └── UnlimitedChain
│ │ ├── key1.json
│ │ ├── key2.json
│ │ ├── key3.json
│ │ ├── key4.json
│ │ └── key.json
├── test_cdpmanager.py
├── test_vault.py
├── test_model.py
├── helpers.py
├── test_sign.py
├── manual_test_dsr.py
├── manual_test_zrxv2.py
├── manual_test_cdpmanager.py
├── dss_token.py
├── manual_test_goerli.py
├── manual_test_tx_recovery.py
├── manual_test_node.py
├── test_savings.py
├── test_feed.py
├── test_auth.py
├── manual_test_mcd.py
├── manual_test_async_tx.py
└── conftest.py
├── .python-version
├── pymaker
├── abi
│ ├── ClipperCallee.bin
│ ├── diff-abi.sh
│ ├── ClipperCallee.abi
│ ├── DSProxyCache.abi
│ ├── ProxyRegistry.abi
│ ├── DSProxyFactory.abi
│ ├── DSAuth.abi
│ ├── DssProxyActionsDsr.abi
│ ├── DSProxyCache.bin
│ ├── TxManager.abi
│ ├── MakerOtcSupportMethods.abi
│ ├── ERC20Token.abi
│ ├── DSValue.abi
│ ├── TokenTransferProxy.abi
│ ├── ZRXToken.abi
│ ├── ESM.abi
│ ├── EtherToken.abi
│ ├── DSProxy.abi
│ ├── DSGuard.abi
│ ├── DaiJoin.abi
│ ├── TokenFaucet.abi
│ ├── DsrManager.abi
│ ├── Pit.abi
│ ├── DSPause.abi
│ ├── GemJoin.abi
│ ├── GemJoin5.abi
│ ├── DSEthToken.abi
│ ├── SaiVox.abi
│ ├── ProxyRegistry.bin
│ ├── DSVault.abi
│ ├── DSRoles.abi
│ ├── Jug.abi
│ ├── Spotter.abi
│ ├── SaiTop.abi
│ ├── Pot.abi
│ ├── DSAuth.bin
│ ├── ExchangeV2-ERC20Proxy.abi
│ ├── OSM.abi
│ ├── Cat.abi
│ ├── SaiTap.abi
│ └── Flapper.abi
├── tightly_packed.py
├── logging.py
├── vault.py
├── oracles.py
├── collateral.py
├── sign.py
├── model.py
├── keys.py
├── ilk.py
├── cdpmanager.py
├── dsrmanager.py
└── auth.py
├── utils
└── etherdelta-client
│ ├── .gitignore
│ ├── package.json
│ └── main.js
├── requirements.txt
├── .gitignore
├── requirements-dev.txt
├── test.sh
├── Makefile
├── test-dss.sh
├── .github
└── workflows
│ └── tests.yaml
├── docker-compose.yml
├── setup.py
├── docs
└── index.rst
└── config
└── testnet-addresses.json
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
1 | 3.6.6
2 |
--------------------------------------------------------------------------------
/tests/accounts/pass:
--------------------------------------------------------------------------------
1 | 12345678
--------------------------------------------------------------------------------
/pymaker/abi/ClipperCallee.bin:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/utils/etherdelta-client/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pytz == 2017.3
2 | web3 == 5.12.0
3 | requests == 2.22.0
4 | eth-keys<0.3.0,>=0.2.1
5 | jsonnet == 0.9.5
6 |
--------------------------------------------------------------------------------
/pymaker/abi/diff-abi.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This script is useful when updating ABIs as newer contracts are released.
4 | vimdiff <(git show HEAD:pymaker/abi/$@ | jq '.') <(jq '.' < $@)
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 |
4 | __pycache__
5 | .cache
6 | .coverage
7 | .pytest_cache
8 |
9 | .DS_Store
10 |
11 | logs/*.json.log
12 | tests/config/keys/UnlimitedChain/address_book.json
13 |
14 | docs/_doc
15 | _virtualenv
16 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | attrs == 19.1.0
2 | codecov == 2.0.9
3 | mock == 2.0.0
4 | pytest == 3.3.0
5 | pytest-asyncio == 0.9.0
6 | pytest-cov == 2.5.1
7 | pytest-mock == 1.6.3
8 | pytest-timeout == 1.2.1
9 | asynctest == 0.13.0
10 | Sphinx == 1.6.2
11 | zipp == 3.4.1
12 |
--------------------------------------------------------------------------------
/pymaker/abi/ClipperCallee.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"clipperCall","outputs":[],"stateMutability":"nonpayable","type":"function"}]
2 |
--------------------------------------------------------------------------------
/tests/abi/OasisMockPriceOracle.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.5.12;
2 |
3 | contract OasisMockPriceOracle {
4 | uint256 price;
5 | function setPrice(address, uint256 _price) public {
6 | price = _price;
7 | }
8 |
9 | function getPriceFor(address, address, uint256) public view returns (uint256) {
10 | return price;
11 | }
12 | }
--------------------------------------------------------------------------------
/pymaker/abi/DSProxyCache.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"_code","type":"bytes"}],"name":"write","outputs":[{"name":"target","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_code","type":"bytes"}],"name":"read","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}]
--------------------------------------------------------------------------------
/utils/etherdelta-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "etherdelta-client",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "AGPL",
11 | "dependencies": {
12 | "minimist": "^1.2.0",
13 | "socket.io-client": "^2.0.4"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tests/accounts/0_0x9596c16d7bf9323265c2f2e22f43e6c80eb3d943.json:
--------------------------------------------------------------------------------
1 | {"version":3,"id":"375c1ad3-b203-4e32-b32a-dd5cad819a4c","address":"9596c16d7bf9323265c2f2e22f43e6c80eb3d943","crypto":{"ciphertext":"4a24fa91bd5c9652ca0a3e9b03c8376d1c4cc1beda2641ea5e2642519b1614c6","cipherparams":{"iv":"39a62791ce1c0e80a3b8a159b1d1c2dd"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"e1e7167139d333de7897971f90f6af69c3befe0a02604775c55d109e82659156","n":262144,"r":8,"p":1},"mac":"945369f4bf11492703ff7793038ab45f7f20a07f559be0b3721f159e8152d00e"}}
--------------------------------------------------------------------------------
/tests/accounts/1_0xe415482ca06eeb684ad3f758c2129fca4b1eb1f4.json:
--------------------------------------------------------------------------------
1 | {"version":3,"id":"d2275fb9-533f-4c63-b970-9767bb45d38d","address":"e415482ca06eeb684ad3f758c2129fca4b1eb1f4","crypto":{"ciphertext":"9807f801eae14580b0641c6474e26e2ac2876f3903d0705965d2c5c5f1cfc4ab","cipherparams":{"iv":"ede22e7e1e3f74560328258df631f7d7"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"9cdab7c0c88539b75946b03a0a3e3d637faf42cf0e99d92540d269b0bc358fa0","n":262144,"r":8,"p":1},"mac":"38c3c786a8b8d61d5169ae854b18c9e4968b6fb596028a71a78fab05477bf889"}}
--------------------------------------------------------------------------------
/tests/accounts/2_0x270b0e8d873e858abd698a000b0da0b94e21d84c.json:
--------------------------------------------------------------------------------
1 | {"version":3,"id":"3088cf66-27a4-4da4-a5de-bb03fdfc1751","address":"270b0e8d873e858abd698a000b0da0b94e21d84c","crypto":{"ciphertext":"92da68991a680c24e806ce49bb3034c63c0597ef0e13b669c5d2bf3c4f8d4d03","cipherparams":{"iv":"758f2d23967c5d2a2b7e816c259647d5"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"e5a6c8b70e5df30da1e53c075b243872c2cc404038a4ac76208aa481a629e377","n":262144,"r":8,"p":1},"mac":"c8316865a833729218c55fac5d709d3d2bfe37c893724c8fa879e9ef3abe0171"}}
--------------------------------------------------------------------------------
/tests/accounts/3_0x812e87be5d4198fca55cb52fa60cb46620617474.json:
--------------------------------------------------------------------------------
1 | {"version":3,"id":"c0870e2f-9f3b-48b6-a670-cd056d3c6931","address":"812e87be5d4198fca55cb52fa60cb46620617474","crypto":{"ciphertext":"03a7f102806b520d17847f6dd75cd0b357fce4cd3097be0f3e40e93e55021b6d","cipherparams":{"iv":"fc81b85e0efded67ae9d6a90283f1886"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"d7e85edd551f7d3fd836dfbd94430c78c2484e51eb5d1d314b3bba21a59a8694","n":262144,"r":8,"p":1},"mac":"6bc6b12ac5e9d2e61069aa76bda242fb2b07f08d07ccfc1437b318f5aaf61dd6"}}
--------------------------------------------------------------------------------
/tests/accounts/4_0x13314e21cd6d343ceb857073f3f6d9368919d1ef.json:
--------------------------------------------------------------------------------
1 | {"version":3,"id":"f9455bbb-482b-46f7-9e29-503838c386be","address":"13314e21cd6d343ceb857073f3f6d9368919d1ef","crypto":{"ciphertext":"e16d398307f5cfc1331db001f1a9b0392eae1bb80299c3b7cf24fc56b3a24755","cipherparams":{"iv":"488f5f4c06909ccec258ff3aa9a40caf"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"f054d8267209ef8e28ef30c3ed3bbb5291ae8738efd0448c7f8993f4c0505388","n":262144,"r":8,"p":1},"mac":"cf3a13bc8e6b6a4794f3890e350b972afe782fe092df7b99e443a34d18ad8ce8"}}
--------------------------------------------------------------------------------
/tests/accounts/5_0x176087fea5c41fc370fabbd850521bc4451690ca.json:
--------------------------------------------------------------------------------
1 | {"version":3,"id":"aaf50a37-d4d1-41ef-ad91-ac5e20a0f5b4","address":"176087fea5c41fc370fabbd850521bc4451690ca","crypto":{"ciphertext":"e9429a2801c7a16ad339bd748fc352639e7380c72fa9bea984b2955664c4350d","cipherparams":{"iv":"5457ac288d8340c984f753e8f21f3bb5"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"5eb1234e8d3ad07a3989c33f68378d7cffd314cee79f988d76623fe083f251f0","n":262144,"r":8,"p":1},"mac":"9470221ed35c56d8798fe238e276ab731b2f14f7671f69fcca16dfd9ccc8bccd"}}
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Pull the docker image
4 | docker pull makerdao/testchain-pymaker:unit-testing
5 |
6 | # Remove existing container if tests not gracefully stopped
7 | docker-compose down
8 |
9 | # Start ganache
10 | docker-compose up -d ganache
11 |
12 | # Start parity and wait to initialize
13 | docker-compose up -d parity
14 | sleep 2
15 |
16 | # Run the tests
17 | py.test --cov=pymaker --cov-report=term --cov-append tests/ $@
18 | TEST_RESULT=$?
19 |
20 | # Cleanup
21 | docker-compose down
22 |
23 | exit $TEST_RESULT
24 |
--------------------------------------------------------------------------------
/tests/config/keys/UnlimitedChain/key1.json:
--------------------------------------------------------------------------------
1 | {"id":"09068027-f04d-f8fb-79d9-9df6f03ac604","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"4ac591b30b76366cf71de779fd32b5fe"},"ciphertext":"6eabb1137e388761f879d85f1f48ff48252ef657ba723e274b04c8b86c676f29","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"e16ce22f791ffa5b8e9da1236e6c943e7988f82ba63a540892e12b5d874241bb"},"mac":"b81515fe8c2ccc669f35b859128bf431c100f6eeeb853f942c73bb7fba9db44c"},"address":"6c626f45e3b7ae5a3998478753634790fd0e82ee","name":"","meta":"{}"}
--------------------------------------------------------------------------------
/tests/config/keys/UnlimitedChain/key2.json:
--------------------------------------------------------------------------------
1 | {"id":"6e7c69aa-e8d3-e381-ed85-36a08df7cfbe","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"e19eee7d928a8d7df75bc58fd31ff05a"},"ciphertext":"b6f9bd7de52fbf59677de26a81b8a4b03bbf4eb0f849e126eae4a36b0ac5a676","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"e9580e3282bcf4bf517d1eb78bf86b41e41bd9777f43afa517b2283c5a52ed9c"},"mac":"906d301903aae013fd98b184f153118a42af29e043865b2845e846eb5c5b4678"},"address":"50ff810797f75f6bfbf2227442e0c961a8562f4c","name":"","meta":"{}"}
--------------------------------------------------------------------------------
/tests/config/keys/UnlimitedChain/key3.json:
--------------------------------------------------------------------------------
1 | {"id":"ecf98a52-7a41-0b18-a39a-e57ddc7a41d9","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"c40c8be2726d367cdc885f59a1158488"},"ciphertext":"0ce8de6e23d2ebdb4ac559af6270e92a01668f11a20868caee672200d7f80c0c","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"b69e7ff92bdb5e10963221ae4379f5a4bfc1e3856001e5a78cf26c1aaeea0d54"},"mac":"c067ac59f17e52586de59a9f4542c11149d84825e9b027f7145455124784337b"},"address":"5beb2d3aa2333a524703af18310acff462c04723","name":"","meta":"{}"}
--------------------------------------------------------------------------------
/tests/config/keys/UnlimitedChain/key4.json:
--------------------------------------------------------------------------------
1 | {"id":"d2bed4ab-c313-7430-be81-8a0d04bfc9aa","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"b7b2722e5e15d41ab83d0350fd72d6f6"},"ciphertext":"7dc899d78c010324b8600a729078d894517c28b5a4fef85e60d0d95c91d51a75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"fa15aef30d031c59d28794c1aec05797d47da1a5a0661d1a8e68841a613881b5"},"mac":"3f8c47c68a3c2428c44674e770dd1c16b47a968a2ec6f5f59901cdbbe994acb0"},"address":"57da1b8f38a5ecf91e9fee8a047df0f0a88716a1","name":"","meta":"{}"}
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | help: ## Show this help.
2 | @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
3 |
4 | doc-clean: ## Clean the documentation output directory
5 | cd docs; rm -rf _doc
6 |
7 | doc-build: doc-clean ## Build the documentation
8 | cd docs; sphinx-build . _doc
9 |
10 | doc-open: doc-build ## Open the documentation
11 | cd docs; open _doc/index.html
12 |
13 | doc-deploy: doc-build ## Deploy the documentation at http://maker-keeper-docs.surge.sh
14 | cd docs; surge --project _doc --domain maker-keeper-docs.surge.sh
15 |
--------------------------------------------------------------------------------
/tests/abi/OasisMockPriceOracle.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getPriceFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"setPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
2 |
--------------------------------------------------------------------------------
/tests/abi/GemMock.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.24;
2 |
3 | import "ds-token/token.sol";
4 |
5 | contract GemMock is DSToken('') {
6 |
7 | constructor(bytes32 symbol_) public {
8 | symbol = symbol_;
9 | }
10 |
11 | function can(address src, address guy) public view returns (bool) {
12 | if (allowance(src, guy) > 0) {
13 | return true;
14 | }
15 |
16 | return false;
17 | }
18 |
19 | function push(bytes32 guy, uint wad) public { push(address(guy), wad); }
20 | function hope(address guy) public { approve(guy); }
21 | function nope(address guy) public { approve(guy, 0); }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/config/keys/UnlimitedChain/key.json:
--------------------------------------------------------------------------------
1 | {"id":"032b64c7-38c6-dfb4-714a-25eacd2daecc","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"38f902a7336bf5160c427f499a6ded5c"},"ciphertext":"afcc67f82dd5327181a75cbd33319301575577f42aa42083f4f6620dcbb6fa60","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"c7b2c641170cfb25a06c60e51d08bdf4e11d51acdfd65aaf832e2d7b13470812"},"mac":"dca205a615725a79073baec0e015a7a5c1d51c72d861081f2c26296abe7cec09"},"address":"00a329c0648769a73afac7f9381e08fb43dbea72","name":"Development Account","meta":"{\"description\":\"Never use this account outside of development chain!\",\"passwordHint\":\"Password is empty string\"}"}
--------------------------------------------------------------------------------
/test-dss.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Pull the docker image
4 | docker pull makerdao/testchain-pymaker:unit-testing
5 |
6 | # Remove existing container if tests not gracefully stopped
7 | docker-compose down
8 |
9 | # Start ganache
10 | docker-compose up -d ganache
11 |
12 | # Start parity and wait to initialize
13 | docker-compose up -d parity
14 | sleep 2
15 |
16 | # Run the tests
17 | py.test --cov=pymaker --cov-report=term --cov-append tests/test_auctions.py tests/test_cdpmanager.py \
18 | tests/test_dsrmanager.py tests/test_dss.py tests/test_savings.py tests/test_shutdown.py $@
19 | TEST_RESULT=$?
20 |
21 | # Cleanup
22 | docker-compose down
23 |
24 | exit $TEST_RESULT
25 |
--------------------------------------------------------------------------------
/tests/abi/OasisMockPriceOracle.bin:
--------------------------------------------------------------------------------
1 | 608060405234801561001057600080fd5b50610157806100206000396000f3fe608060405234801561001057600080fd5b50600436106100355760003560e01c8062e4768b1461003a578063413713c114610088575b600080fd5b6100866004803603604081101561005057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061010a565b005b6100f46004803603606081101561009e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610115565b6040518082815260200191505060405180910390f35b806000819055505050565b600080549050939250505056fea265627a7a723158202ed147b02eb302020ecb419b6d41d429ebf14caf75ed2c3dfd1f1b7763abe66064736f6c634300050c0032
--------------------------------------------------------------------------------
/pymaker/abi/ProxyRegistry.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"factory_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":false,"inputs":[],"name":"build","outputs":[{"internalType":"address payable","name":"proxy","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"build","outputs":[{"internalType":"address payable","name":"proxy","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"proxies","outputs":[{"internalType":"contract DSProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | # Trigger the workflow on push or pull request,
3 | # but only for the main branch
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 |
16 | - name: Checkout repository and submodules
17 | uses: actions/checkout@v3
18 | with:
19 | submodules: recursive
20 |
21 | - name: setup python
22 | uses: actions/setup-python@v4
23 | with:
24 | python-version: '3.8'
25 |
26 | - name: install python packages
27 | run: |
28 | python -m pip install --upgrade pip
29 | pip install virtualenv --upgrade
30 | pip install -r requirements.txt
31 | pip install -r requirements-dev.txt
32 | - name: execute tests
33 | run: ./test.sh
34 |
--------------------------------------------------------------------------------
/pymaker/abi/DSProxyFactory.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isProxy","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cache","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"build","outputs":[{"name":"proxy","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"build","outputs":[{"name":"proxy","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"proxy","type":"address"},{"indexed":false,"name":"cache","type":"address"}],"name":"Created","type":"event"}]
--------------------------------------------------------------------------------
/pymaker/abi/DSAuth.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.2"
2 | services:
3 | parity:
4 | image: makerdao/testchain-pymaker:unit-testing-2.0.0
5 | container_name: parity-pymaker-test
6 | ports:
7 | - "8545:8545"
8 | - "8546:8546"
9 | expose:
10 | - "8545"
11 | - "8546"
12 | user: root
13 | working_dir: /home/parity
14 |
15 | ganache:
16 | image: trufflesuite/ganache-cli:v6.9.1
17 | container_name: ganache
18 | ports:
19 | - "8555:8555"
20 | expose:
21 | - "8555"
22 | command: "--gasLimit 10000000
23 | -p 8555
24 | --account=\"0x91cf2cc3671a365fcbf38010ff97ee31a5b7e674842663c56769e41600696ead,1000000000000000000000000\"
25 | --account=\"0xc0a550404067ce46a51283e0cc99ec3ba832940064587147a8db9a7ba355ef27,1000000000000000000000000\",
26 | --account=\"0x6ca1cfaba9715aa485504cb8a3d3fe54191e0991b5f47eb982e8fb40d1b8e8d8,1000000000000000000000000\",
27 | --account=\"0x1a9e422172e3d84487f7c833e3895f2f65c35eff7e68783adaa0c5bbe741ca8a,1000000000000000000000000\""
28 |
29 |
--------------------------------------------------------------------------------
/pymaker/abi/DssProxyActionsDsr.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"internalType":"address","name":"apt","type":"address"},{"internalType":"address","name":"urn","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"daiJoin_join","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"daiJoin","type":"address"},{"internalType":"address","name":"pot","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"daiJoin","type":"address"},{"internalType":"address","name":"pot","type":"address"}],"name":"exitAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"daiJoin","type":"address"},{"internalType":"address","name":"pot","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"join","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
2 |
--------------------------------------------------------------------------------
/pymaker/tightly_packed.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | from eth_abi.encoding import encode_uint_256, BytesEncoder, AddressEncoder
19 |
20 | from pymaker import Address
21 |
22 |
23 | def encode_address(address: Address) -> bytes:
24 | return AddressEncoder().encode(address.address)[12:]
25 |
26 |
27 | def encode_uint256(value: int) -> bytes:
28 | return encode_uint_256.encode(value)
29 |
30 |
31 | def encode_bytes(value: bytes) -> bytes:
32 | return BytesEncoder().encode(value)
33 |
--------------------------------------------------------------------------------
/pymaker/abi/DSProxyCache.bin:
--------------------------------------------------------------------------------
1 | 608060405234801561001057600080fd5b5061029c806100206000396000f30060806040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416637ed0c3b281146100505780638bf4515c146100d2575b600080fd5b34801561005c57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526100a994369492936024939284019190819084018382808284375094975061012b9650505050505050565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156100de57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526100a99436949293602493928401919081908401838280828437509497506101e79650505050505050565b6000808251602084016000f09150813b156001811461004b5750826040518082805190602001908083835b602083106101755780518252601f199092019160209182019101610156565b51815160209384036101000a6000190180199092169116179052604080519290940182900390912060009081529081905291909120805473ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff87161790555092949350505050565b600080826040518082805190602001908083835b6020831061021a5780518252601f1990920191602091820191016101fb565b51815160209384036101000a60001901801990921691161790526040805192909401829003909120600090815290819052919091205473ffffffffffffffffffffffffffffffffffffffff1696955050505050505600a165627a7a72305820105083b62f0f9cd6be84fcbd3c7208d0d48d7235d9538890eac2369f4864414d0029
--------------------------------------------------------------------------------
/pymaker/abi/TxManager.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokens","type":"address[]"},{"name":"script","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/tests/abi/DaiMock.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"can","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"hope","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"move","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"nope","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"urn","type":"bytes32"},{"name":"wad","type":"uint256"}],"name":"join","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"vat_","type":"address"},{"name":"dai_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
--------------------------------------------------------------------------------
/pymaker/abi/MakerOtcSupportMethods.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[{"name":"otc","type":"address"},{"name":"payToken","type":"address"},{"name":"buyToken","type":"address"}],"name":"getOffers","outputs":[{"name":"ids","type":"uint256[100]"},{"name":"payAmts","type":"uint256[100]"},{"name":"buyAmts","type":"uint256[100]"},{"name":"owners","type":"address[100]"},{"name":"timestamps","type":"uint256[100]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"otc","type":"address"},{"name":"buyToken","type":"address"},{"name":"buyAmt","type":"uint256"},{"name":"payToken","type":"address"}],"name":"getOffersAmountToBuyAll","outputs":[{"name":"ordersToTake","type":"uint256"},{"name":"takesPartialOrder","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"otc","type":"address"},{"name":"offerId","type":"uint256"}],"name":"getOffers","outputs":[{"name":"ids","type":"uint256[100]"},{"name":"payAmts","type":"uint256[100]"},{"name":"buyAmts","type":"uint256[100]"},{"name":"owners","type":"address[100]"},{"name":"timestamps","type":"uint256[100]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"otc","type":"address"},{"name":"payToken","type":"address"},{"name":"payAmt","type":"uint256"},{"name":"buyToken","type":"address"}],"name":"getOffersAmountToSellAll","outputs":[{"name":"ordersToTake","type":"uint256"},{"name":"takesPartialOrder","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]
--------------------------------------------------------------------------------
/pymaker/abi/ERC20Token.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"guy","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"supply","type":"uint256"}],"payable":false,"type":"constructor"},{"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"},{"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"}]
--------------------------------------------------------------------------------
/pymaker/abi/DSValue.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wut","type":"bytes32"}],"name":"poke","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"read","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"peek","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"void","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/pymaker/abi/TokenTransferProxy.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"target","type":"address"}],"name":"addAuthorizedAddress","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"authorities","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"target","type":"address"}],"name":"removeAuthorizedAddress","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"authorized","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getAuthorizedAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"target","type":"address"},{"indexed":true,"name":"caller","type":"address"}],"name":"LogAuthorizedAddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"target","type":"address"},{"indexed":true,"name":"caller","type":"address"}],"name":"LogAuthorizedAddressRemoved","type":"event"}]
--------------------------------------------------------------------------------
/tests/abi/DaiMock.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.24;
2 |
3 | // Fusion between a DaiJoin and a DaiMove
4 |
5 | contract GemLike {
6 | function transferFrom(address,address,uint) public returns (bool);
7 | function mint(address,uint) public;
8 | function burn(address,uint) public;
9 | }
10 |
11 | contract VatLike {
12 | function slip(bytes32,bytes32,int) public;
13 | function move(bytes32,bytes32,int) public;
14 | function flux(bytes32,bytes32,bytes32,int) public;
15 | }
16 |
17 | contract DaiMock {
18 | VatLike public vat;
19 | GemLike public dai;
20 | constructor(address vat_, address dai_) public {
21 | vat = VatLike(vat_);
22 | dai = GemLike(dai_);
23 | }
24 | uint constant ONE = 10 ** 27;
25 | function mul(uint x, uint y) internal pure returns (int z) {
26 | z = int(x * y);
27 | require(int(z) >= 0);
28 | require(y == 0 || uint(z) / y == x);
29 | }
30 | mapping(address => mapping (address => bool)) public can;
31 | function hope(address guy) public { can[msg.sender][guy] = true; }
32 | function nope(address guy) public { can[msg.sender][guy] = false; }
33 | function move(address src, address dst, uint wad) public {
34 | require(src == msg.sender || can[src][msg.sender]);
35 | vat.move(bytes32(src), bytes32(dst), mul(ONE, wad));
36 | }
37 | function join(bytes32 urn, uint wad) public {
38 | vat.move(bytes32(address(this)), urn, mul(ONE, wad));
39 | dai.burn(msg.sender, wad);
40 | }
41 | function exit(address guy, uint wad) public {
42 | vat.move(bytes32(msg.sender), bytes32(address(this)), mul(ONE, wad));
43 | dai.mint(guy, wad);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/pymaker/abi/ZRXToken.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"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,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"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"},{"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"}]
--------------------------------------------------------------------------------
/tests/test_cdpmanager.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2020 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | from pymaker import Address
19 | from pymaker.deployment import DssDeployment
20 | from pymaker.cdpmanager import Urn
21 |
22 |
23 | class TestCdpManager:
24 |
25 | def test_none(self, our_address: Address, mcd: DssDeployment):
26 | assert mcd.cdp_manager.first(our_address) == 0
27 | assert mcd.cdp_manager.last(our_address) == 0
28 | assert mcd.cdp_manager.count(our_address) == 0
29 |
30 | def test_open(self, our_address: Address, mcd: DssDeployment):
31 | ilk = mcd.collaterals['ETH-A'].ilk
32 | assert mcd.cdp_manager.open(ilk, our_address).transact()
33 | assert mcd.cdp_manager.last(our_address) == 1
34 | assert mcd.cdp_manager.ilk(1).name == ilk.name
35 | assert mcd.cdp_manager.owns(1) == our_address
36 | assert isinstance(mcd.cdp_manager.urn(1), Urn)
37 |
38 | def test_one(self, our_address: Address, mcd: DssDeployment):
39 | assert mcd.cdp_manager.first(our_address) == 1
40 | assert mcd.cdp_manager.last(our_address) == 1
41 | assert mcd.cdp_manager.count(our_address) == 1
42 |
--------------------------------------------------------------------------------
/tests/test_vault.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import pytest
19 | from web3 import Web3, HTTPProvider
20 |
21 | from pymaker import Address
22 | from pymaker.vault import DSVault
23 |
24 |
25 | class TestDSVault:
26 | def setup_method(self):
27 | self.web3 = Web3(HTTPProvider("http://localhost:8555"))
28 | self.web3.eth.defaultAccount = self.web3.eth.accounts[0]
29 | self.our_address = Address(self.web3.eth.defaultAccount)
30 | self.dsvault = DSVault.deploy(self.web3)
31 |
32 | def test_fail_when_no_contract_under_that_address(self):
33 | # expect
34 | with pytest.raises(Exception):
35 | DSVault(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))
36 |
37 | def test_authority(self):
38 | # given
39 | some_address = Address('0x0000000000111111111122222222223333333333')
40 |
41 | # when
42 | self.dsvault.set_authority(some_address).transact()
43 |
44 | # then
45 | assert self.dsvault.authority() == some_address
46 |
47 | def test_should_have_printable_representation(self):
48 | assert repr(self.dsvault) == f"DSVault('{self.dsvault.address}')"
49 |
--------------------------------------------------------------------------------
/pymaker/abi/ESM.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"gem_","type":"address"},{"internalType":"address","name":"end_","type":"address"},{"internalType":"address","name":"proxy_","type":"address"},{"internalType":"uint256","name":"min_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[],"name":"Fire","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Join","type":"event"},{"inputs":[],"name":"Sum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"deny","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"end","outputs":[{"internalType":"contract EndLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gem","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"join","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"min","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"revokesGovernanceAccess","outputs":[{"internalType":"bool","name":"ret","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"sum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/pymaker/abi/EtherToken.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"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,"type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"payable":true,"type":"fallback"},{"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"},{"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"}]
--------------------------------------------------------------------------------
/tests/test_model.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2020 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | from pymaker import Address, Wad
19 | from pymaker.model import Token
20 |
21 |
22 | class TestToken:
23 | def setup_class(self):
24 | self.token = Token("COW", Address('0xbeef00000000000000000000000000000000BEEF'), 4)
25 |
26 | def test_convert(self):
27 | # two
28 | chain_amount = Wad(20000)
29 | assert self.token.normalize_amount(chain_amount) == Wad.from_number(2)
30 |
31 | # three
32 | normalized_amount = Wad.from_number(3)
33 | assert self.token.unnormalize_amount(normalized_amount) == Wad(30000)
34 |
35 | def test_min_amount(self):
36 | assert self.token.min_amount == Wad.from_number(0.0001)
37 | assert float(self.token.min_amount) == 0.0001
38 | assert self.token.unnormalize_amount(self.token.min_amount) == Wad(1)
39 |
40 | assert Wad.from_number(0.0004) > self.token.min_amount
41 | assert Wad.from_number(0.00005) < self.token.min_amount
42 |
43 | assert self.token.unnormalize_amount(Wad.from_number(0.0006)) > self.token.unnormalize_amount(self.token.min_amount)
44 | assert self.token.unnormalize_amount(Wad.from_number(0.00007)) < self.token.unnormalize_amount(self.token.min_amount)
45 | assert self.token.unnormalize_amount(Wad.from_number(0.00008)) == Wad(0)
46 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | """A setuptools based setup module.
2 |
3 | See:
4 | https://packaging.python.org/guides/distributing-packages-using-setuptools/
5 | https://github.com/pypa/sampleproject
6 | https://github.com/pypa/sampleproject/blob/master/setup.py
7 | """
8 |
9 | # Always prefer setuptools over distutils
10 | from setuptools import setup, find_packages
11 | from os import path
12 |
13 | here = path.abspath(path.dirname(__file__))
14 |
15 | # Get the long description from the README file
16 | with open(path.join(here, 'README.md'), encoding='utf-8') as f:
17 | long_description = f.read()
18 |
19 | # Read requirements.txt
20 | with open(path.join(here, 'requirements.txt'), encoding='utf-8') as f:
21 | requirements = f.read().split('\n')
22 |
23 | # Arguments marked as "Required" below must be included for upload to PyPI.
24 | # Fields marked as "Optional" may be commented out.
25 |
26 | setup(
27 | name='pymaker',
28 |
29 | # Versions should comply with PEP 440:
30 | # https://www.python.org/dev/peps/pep-0440/
31 | #
32 | # For a discussion on single-sourcing the version across setup.py and the
33 | # project code, see
34 | # https://packaging.python.org/en/latest/single_source_version.html
35 | version='1.1.3', # Required
36 | description='Python API for Maker contracts',
37 | license='COPYING',
38 | long_description=long_description,
39 | long_description_content_type='text/markdown',
40 | url='https://github.com/makerdao/pymaker',
41 | author='MakerDAO',
42 | packages=find_packages(include=['pymaker', 'pymaker.*']), # Required
43 | package_data={'pymaker': ['abi/*', '../config/*']},
44 | include_package_data=True,
45 | python_requires='~=3.6',
46 |
47 | # This field lists other packages that your project depends on to run.
48 | # Any package you put here will be installed by pip when your project is
49 | # installed, so they must be valid existing projects.
50 | #
51 | # For an analysis of "install_requires" vs pip's requirements files see:
52 | # https://packaging.python.org/en/latest/requirements.html
53 | install_requires=requirements
54 | )
55 |
--------------------------------------------------------------------------------
/pymaker/abi/DSProxy.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_target","type":"address"},{"name":"_data","type":"bytes"}],"name":"execute","outputs":[{"name":"response","type":"bytes32"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_code","type":"bytes"},{"name":"_data","type":"bytes"}],"name":"execute","outputs":[{"name":"target","type":"address"},{"name":"response","type":"bytes32"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"cache","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_cacheAddr","type":"address"}],"name":"setCache","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_cacheAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/pymaker/abi/DSGuard.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"sig","type":"bytes32"}],"name":"forbid","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"bytes32"},{"name":"dst","type":"bytes32"},{"name":"sig","type":"bytes32"}],"name":"forbid","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"ANY","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src_","type":"address"},{"name":"dst_","type":"address"},{"name":"sig","type":"bytes4"}],"name":"canCall","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"sig","type":"bytes32"}],"name":"permit","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"bytes32"},{"name":"dst","type":"bytes32"},{"name":"sig","type":"bytes32"}],"name":"permit","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"bytes32"},{"indexed":true,"name":"dst","type":"bytes32"},{"indexed":true,"name":"sig","type":"bytes32"}],"name":"LogPermit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"bytes32"},{"indexed":true,"name":"dst","type":"bytes32"},{"indexed":true,"name":"sig","type":"bytes32"}],"name":"LogForbid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/utils/etherdelta-client/main.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * This file is part of Maker Keeper Framework.
3 | *
4 | * Copyright (C) 2017-2018 reverendus
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Affero General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Affero General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Affero General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | var args = require('minimist')(process.argv.slice(2));
21 | const order = args['_'].join(" ");
22 | const url = args['url'];
23 | const retryInterval = args['retry-interval'];
24 | const timeout = args['timeout'];
25 |
26 | function publishOrder() {
27 | socket.emit('message', JSON.parse(order));
28 | console.log('Order sent');
29 | }
30 |
31 | console.log("Sending order '" + order + "' to " + url);
32 |
33 | const io = require('socket.io-client');
34 | const socket = io.connect(url, { transports: ['websocket'] });
35 |
36 | socket.on('connect', () => {
37 | console.log("Connected to socket");
38 | publishOrder();
39 | });
40 |
41 | socket.on('messageResult', (messageResult) => {
42 | console.log("Response received: ", messageResult);
43 |
44 | if (messageResult[0] === 'Added/updated order.') {
45 | console.log("Order placed successfully");
46 | socket.disconnect();
47 | setTimeout(() => process.exit(0), 2500);
48 | }
49 | else {
50 | console.log("Order placement failed");
51 | setTimeout(publishOrder, retryInterval*1000);
52 | }
53 | });
54 |
55 |
56 | socket.on('disconnect', () => {
57 | console.log('Disconnected from socket');
58 | });
59 |
60 | socket.on('reconnect', () => {
61 | console.log('Reconnected to socket');
62 | });
63 |
64 | setTimeout(() => {
65 | console.log('Timed out');
66 | process.exit(-1);
67 | }, timeout*1000);
68 |
--------------------------------------------------------------------------------
/tests/helpers.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import time
19 | from unittest.mock import Mock
20 |
21 | from web3 import Web3
22 |
23 |
24 | def is_hashable(v):
25 | """Determine whether `v` can be hashed."""
26 | try:
27 | hash(v)
28 | except TypeError:
29 | return False
30 | return True
31 |
32 |
33 | def wait_until_mock_called(mock: Mock):
34 | while not mock.called:
35 | pass
36 | return mock.call_args[0]
37 |
38 |
39 | def time_travel_by(web3: Web3, seconds: int):
40 | assert(isinstance(web3, Web3))
41 | assert(isinstance(seconds, int))
42 |
43 | if "Parity" in web3.clientVersion:
44 | print(f"time travel unsupported by parity; waiting {seconds} seconds")
45 | time.sleep(seconds)
46 | # force a block mining to have a correct timestamp in latest block
47 | web3.eth.sendTransaction({'from': web3.eth.accounts[0], 'to': web3.eth.accounts[1], 'value': 1})
48 | else:
49 | web3.manager.request_blocking("evm_increaseTime", [seconds])
50 | # force a block mining to have a correct timestamp in latest block
51 | web3.manager.request_blocking("evm_mine", [])
52 |
53 |
54 | def snapshot(web3: Web3):
55 | assert(isinstance(web3, Web3))
56 |
57 | return web3.manager.request_blocking("evm_snapshot", [])
58 |
59 |
60 | def reset(web3: Web3, snap_id):
61 | assert(isinstance(web3, Web3))
62 |
63 | return web3.manager.request_blocking("evm_revert", [snap_id])
64 |
--------------------------------------------------------------------------------
/pymaker/abi/DaiJoin.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"vat_","type":"address"},{"internalType":"address","name":"dai_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"constant":false,"inputs":[],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract DSTokenLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"join","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"live","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/pymaker/abi/TokenFaucet.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"amt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"done","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"gem","type":"address"},{"internalType":"address[]","name":"addrs","type":"address[]"}],"name":"gulp","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"gem","type":"address"}],"name":"gulp","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"gem","type":"address"},{"internalType":"uint256","name":"amt_","type":"uint256"}],"name":"setAmt","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract ERC20Like","name":"gem","type":"address"}],"name":"shut","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/pymaker/abi/DsrManager.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"pot_","type":"address"},{"internalType":"address","name":"daiJoin_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Join","type":"event"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"daiBalance","outputs":[{"internalType":"uint256","name":"wad","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"daiJoin","outputs":[{"internalType":"contract JoinLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"dst","type":"address"}],"name":"exitAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"join","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pieOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pot","outputs":[{"internalType":"contract PotLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/tests/test_sign.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import pkg_resources
19 | from web3 import Web3, HTTPProvider
20 |
21 | from pymaker import Address
22 | from pymaker.keys import register_key_file
23 | from pymaker.sign import eth_sign
24 |
25 |
26 | def test_signing():
27 | # given
28 | web3 = Web3(HTTPProvider("http://localhost:8555"))
29 | web3.eth.defaultAccount = web3.eth.accounts[0]
30 |
31 | # and
32 | text = "abc"
33 | msg = bytes(text, 'utf-8')
34 |
35 | # expect
36 | assert eth_sign(msg, web3).startswith("0x")
37 |
38 |
39 | def test_signing_with_key_and_rpc_should_return_same_result():
40 | # given
41 | web3 = Web3(HTTPProvider("http://localhost:8555"))
42 | web3.eth.defaultAccount = web3.eth.accounts[0]
43 |
44 | assert Address(web3.eth.defaultAccount) == Address('0x9596c16d7bf9323265c2f2e22f43e6c80eb3d943')
45 |
46 | # and
47 | text = "abc"
48 | msg = bytes(text, 'utf-8')
49 |
50 | rpc_signature = eth_sign(msg, web3)
51 |
52 | # when
53 | keyfile_path = pkg_resources.resource_filename(__name__, "accounts/0_0x9596c16d7bf9323265c2f2e22f43e6c80eb3d943.json")
54 | passfile_path = pkg_resources.resource_filename(__name__, "accounts/pass")
55 |
56 | register_key_file(web3, keyfile_path, passfile_path)
57 |
58 | # and
59 | # [we do this in order to make sure that the message was signed using the local key]
60 | # [with `request_blocking` set to `None` any http requests will basically fail]
61 | web3.manager.request_blocking = None
62 |
63 | # and
64 | local_signature = eth_sign(msg, web3)
65 |
66 | # then
67 | assert rpc_signature == local_signature
68 |
--------------------------------------------------------------------------------
/tests/manual_test_dsr.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2019 grandizzy
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 |
19 | import sys
20 | from web3 import Web3, HTTPProvider
21 |
22 | from pymaker import Address
23 | from pymaker.deployment import DssDeployment
24 | from pymaker.keys import register_keys
25 | from pymaker.numeric import Wad
26 | from pymaker.dsr import Dsr
27 |
28 |
29 | web3 = Web3(HTTPProvider(endpoint_uri="http://0.0.0.0:8545",
30 | request_kwargs={"timeout": 10}))
31 | web3.eth.defaultAccount = sys.argv[1] # ex: 0x0000000000000000000000000000000aBcdef123
32 | register_keys(web3, [sys.argv[2]]) # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
33 |
34 | mcd = DssDeployment.from_network(web3, "kovan")
35 | our_address = Address(web3.eth.defaultAccount)
36 | print(our_address)
37 |
38 | dsr_client = Dsr(mcd, our_address)
39 |
40 | print(f"Chi: {dsr_client.chi()}")
41 | print(f"Total DAI: {dsr_client.get_total_dai()}")
42 | print(f"DSR: {dsr_client.dsr()}")
43 |
44 | proxy = dsr_client.get_proxy()
45 | print(f"Has Proxy: {dsr_client.has_proxy()}")
46 |
47 | if not dsr_client.has_proxy():
48 | dsr_client.build_proxy().transact()
49 |
50 | proxy = dsr_client.get_proxy()
51 | print(f"Proxy address: {proxy.address.address}")
52 |
53 | print(f"Balance: {dsr_client.get_balance(proxy.address)}")
54 |
55 | # approve proxy to use 10 DAI from account
56 | dsr_client.mcd.dai.approve(proxy.address, Wad.from_number(10)).transact()
57 |
58 | dsr_client.join(Wad.from_number(2.2), proxy).transact()
59 | print(f"Balance: {dsr_client.get_balance(proxy.address)}")
60 |
61 | dsr_client.exit(Wad.from_number(1.01), proxy).transact()
62 | print(f"Balance: {dsr_client.get_balance(proxy.address)}")
63 |
64 | dsr_client.exit_all(proxy).transact()
65 | print(f"Balance: {dsr_client.get_balance(proxy.address)}")
66 |
67 |
--------------------------------------------------------------------------------
/pymaker/abi/Pit.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"frob","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"live","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"Line","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"spot","type":"uint256"},{"name":"line","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"ilk","type":"bytes32"},{"indexed":true,"name":"urn","type":"bytes32"},{"indexed":false,"name":"ink","type":"uint256"},{"indexed":false,"name":"art","type":"uint256"},{"indexed":false,"name":"dink","type":"int256"},{"indexed":false,"name":"dart","type":"int256"},{"indexed":false,"name":"iInk","type":"uint256"},{"indexed":false,"name":"iArt","type":"uint256"}],"name":"Frob","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"}]
--------------------------------------------------------------------------------
/pymaker/abi/DSPause.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"usr","type":"address"},{"name":"fax","type":"bytes"},{"name":"era","type":"uint256"}],"name":"drop","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"delay","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"usr","type":"address"},{"name":"fax","type":"bytes"},{"name":"era","type":"uint256"}],"name":"plan","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"plans","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"usr","type":"address"},{"name":"fax","type":"bytes"},{"name":"era","type":"uint256"}],"name":"exec","outputs":[{"name":"response","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"delay_","type":"uint256"},{"name":"owner_","type":"address"},{"name":"authority_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"usr","type":"address"},{"indexed":false,"name":"fax","type":"bytes"},{"indexed":false,"name":"era","type":"uint256"}],"name":"Plan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"usr","type":"address"},{"indexed":false,"name":"fax","type":"bytes"},{"indexed":false,"name":"era","type":"uint256"}],"name":"Drop","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"usr","type":"address"},{"indexed":false,"name":"fax","type":"bytes"},{"indexed":false,"name":"era","type":"uint256"}],"name":"Exec","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/tests/manual_test_zrxv2.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import sys
19 | import time
20 |
21 | from web3 import EthereumTesterProvider, Web3, HTTPProvider
22 |
23 | from pymaker import Address
24 | from pymaker.approval import directly
25 | from pymaker.numeric import Wad
26 | from pymaker.token import DSToken, ERC20Token
27 | from pymaker.util import bytes_to_hexstring
28 | from pymaker.zrxv2 import ZrxExchangeV2, Order, ZrxRelayerApiV2, ERC20Asset
29 | from tests.helpers import is_hashable, wait_until_mock_called
30 |
31 | #EXCHANGE_ADDR = '0x4f833a24e1f95d70f028921e27040ca56e09ab0b' # Mainnet
32 | EXCHANGE_ADDR = sys.argv[1]
33 | SRAV2_URL = 'https://kovan-staging.ercdex.com/api'
34 |
35 | KOVAN_DAI = Address('0xc4375b7de8af5a38a93548eb8453a498222c4ff2')
36 | KOVAN_WETH = Address('0xd0a1e359811322d97991e03f863a0c30c2cf029c')
37 |
38 |
39 | web3 = Web3(HTTPProvider('http://localhost:8545'))
40 | web3.eth.defaultAccount = web3.eth.accounts[0]
41 |
42 | exchange = ZrxExchangeV2(web3=web3, address=Address(EXCHANGE_ADDR))
43 | #exchange.approve([ERC20Token(web3=web3, address=KOVAN_DAI),
44 | # ERC20Token(web3=web3, address=KOVAN_WETH)], directly())
45 |
46 | order = exchange.create_order(pay_asset=ERC20Asset(KOVAN_WETH),
47 | pay_amount=Wad.from_number(0.1),
48 | buy_asset=ERC20Asset(KOVAN_DAI),
49 | buy_amount=Wad.from_number(25),
50 | expiration=int(time.time())+60*35)
51 |
52 | api = ZrxRelayerApiV2(exchange=exchange, api_server=SRAV2_URL)
53 | order = api.configure_order(order)
54 | order = exchange.sign_order(order)
55 | #print(order)
56 |
57 | #print(api.submit_order(order))
58 | #print(api.get_orders(KOVAN_WETH, KOVAN_DAI))
59 | print(api.get_orders_by_maker(Address(web3.eth.defaultAccount)))
60 |
61 | #print(exchange.past_fill(500))
62 |
--------------------------------------------------------------------------------
/pymaker/abi/GemJoin.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"vat_","type":"address"},{"internalType":"bytes32","name":"ilk_","type":"bytes32"},{"internalType":"address","name":"gem_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"constant":false,"inputs":[],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"dec","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"gem","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ilk","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"join","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"live","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/pymaker/abi/GemJoin5.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"vat_","type":"address"},{"internalType":"bytes32","name":"ilk_","type":"bytes32"},{"internalType":"address","name":"gem_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"constant":false,"inputs":[],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"dec","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"gem","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ilk","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"urn","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"join","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"live","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/pymaker/abi/DSEthToken.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[],"name":"wrap","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"guy","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"unwrap","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"tryWithdraw","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Withdrawal","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"},{"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"}]
--------------------------------------------------------------------------------
/pymaker/abi/SaiVox.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[],"name":"prod","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"era","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"how","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"par","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"ray","type":"uint256"}],"name":"tell","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"way","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"param","type":"bytes32"},{"name":"val","type":"uint256"}],"name":"mold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"fix","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"ray","type":"uint256"}],"name":"tune","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tau","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"par_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/tests/manual_test_cdpmanager.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2020 ith-harvey
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 |
19 | import sys
20 | import os
21 | from web3 import Web3, HTTPProvider
22 |
23 | from pymaker import Address
24 | from pymaker.deployment import DssDeployment
25 | from pymaker.keys import register_keys
26 | from pymaker.numeric import Wad
27 | from pymaker.dsr import Dsr
28 |
29 | endpoint_uri = f"{os.environ['SERVER_ETH_RPC_HOST']}:{os.environ['SERVER_ETH_RPC_PORT']}"
30 | web3 = Web3(HTTPProvider(endpoint_uri=endpoint_uri,
31 | request_kwargs={"timeout": 10}))
32 | web3.eth.defaultAccount = sys.argv[1] # ex: 0x0000000000000000000000000000000aBcdef123
33 | register_keys(web3, [sys.argv[2]]) # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
34 | cdpid = int(sys.argv[3])
35 |
36 | mcd = DssDeployment.from_network(web3, "kovan")
37 | our_address = Address(web3.eth.defaultAccount)
38 | dsr_client = Dsr(mcd, our_address)
39 | print(our_address)
40 |
41 | print(f"Default account: {our_address.address}")
42 | if dsr_client.has_proxy():
43 | proxy = dsr_client.get_proxy()
44 | print(f"{our_address} has a DS-Proxy - {proxy.address.address}, test will continue")
45 |
46 | print(f"Urn of Cdp ID {cdpid} - {mcd.cdp_manager.urn(cdpid)}")
47 | print(f"Owner of CDP ID {cdpid} - {mcd.cdp_manager.owns(cdpid)}")
48 | print(f"List of CDP IDs next to and previous to {cdpid} - {mcd.cdp_manager.list(cdpid)}")
49 | print(f"Ilk of CDP ID {cdpid} - {mcd.cdp_manager.ilk(cdpid)}")
50 |
51 | print(f"First of Cdp ID for account {proxy.address.address} - {mcd.cdp_manager.first(proxy.address)}")
52 | print(f"Last of Cdp ID for account {proxy.address.address} - {mcd.cdp_manager.last(proxy.address)}")
53 | print(f"Number of all CDPs created via DS-Cdp-Manager contract {proxy.address.address} - {mcd.cdp_manager.count(proxy.address)}")
54 |
55 | else:
56 | print(f"{our_address} does not have a DS-Proxy. Please create a cdp on kovan via Oasis.app (to create a proxy) to perform this test")
57 |
58 |
59 |
--------------------------------------------------------------------------------
/pymaker/logging.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2019 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import logging
19 | from pprint import pformat
20 | from web3 import Web3
21 | from web3._utils.events import get_event_data
22 |
23 | from eth_abi.codec import ABICodec
24 | from eth_abi.registry import registry as default_registry
25 |
26 | # Shared between DSNote and many MCD contracts
27 | class LogNote:
28 | def __init__(self, log):
29 | args = log['args']
30 | self.sig = Web3.toHex(args['sig'])
31 | self.usr = args['usr'] if 'usr' in args else None # vat.frob doesn't offer `usr`
32 | self.arg1 = args['arg1'] if 'arg1' in args else None
33 | self.arg2 = args['arg2'] if 'arg2' in args else None
34 | self.arg3 = args['arg3'] if 'arg3' in args else None # Special variant used for vat.frob
35 | self.block = log['blockNumber']
36 | self.tx_hash = log['transactionHash'].hex()
37 | self._data = args['data']
38 |
39 | @classmethod
40 | def from_event(cls, event: dict, contract_abi: list):
41 | assert isinstance(event, dict)
42 | assert isinstance(contract_abi, list)
43 |
44 | log_note_abi = [abi for abi in contract_abi if abi.get('name') == 'LogNote'][0]
45 | try:
46 | codec = ABICodec(default_registry)
47 | event_data = get_event_data(codec, log_note_abi, event)
48 | return LogNote(event_data)
49 | except ValueError:
50 | # event is not a LogNote
51 | return None
52 |
53 | def get_bytes_at_index(self, index: int) -> bytes:
54 | assert isinstance(index, int)
55 | if index > 5:
56 | raise ValueError("Only six words of calldata are provided")
57 |
58 | start_index = len(self._data) - ((6-index) * 32) - 28
59 | return self._data[start_index:start_index+32]
60 |
61 | def __eq__(self, other):
62 | assert isinstance(other, LogNote)
63 | return self.__dict__ == other.__dict__
64 |
65 | def __repr__(self):
66 | return f"LogNote({pformat(vars(self))})"
67 |
--------------------------------------------------------------------------------
/pymaker/abi/ProxyRegistry.bin:
--------------------------------------------------------------------------------
1 | 608060405234801561001057600080fd5b506040516105b23803806105b28339818101604052602081101561003357600080fd5b810190808051906020019092919050505080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061051d806100956000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80638e1a55fc14610046578063c455279114610090578063f3701da214610114575b600080fd5b61004e610198565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100d2600480360360208110156100a657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506101a8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101566004803603602081101561012a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506101db565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60006101a3336101db565b905090565b60006020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008073ffffffffffffffffffffffffffffffffffffffff166000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16148061037f57508173ffffffffffffffffffffffffffffffffffffffff166000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561032b57600080fd5b505afa15801561033f573d6000803e3d6000fd5b505050506040513d602081101561035557600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614155b61038857600080fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f3701da2836040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b15801561042957600080fd5b505af115801561043d573d6000803e3d6000fd5b505050506040513d602081101561045357600080fd5b81019080805190602001909291905050509050806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091905056fea265627a7a72315820dde22e20d59bcfe01bcafda726adc265175924ee37c24a292c4361b64a6ffa3364736f6c634300050c0032
2 |
--------------------------------------------------------------------------------
/tests/dss_token.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 | from web3 import Web3
18 |
19 | from pymaker import Contract, Address, Transact
20 | from pymaker.dss import Urn, Ilk
21 | from pymaker.numeric import Wad
22 |
23 |
24 | class GemMock(Contract):
25 | """A client for `GemMock` contract.
26 | """
27 |
28 | abi = Contract._load_abi(__name__, 'abi/GemMock.abi')
29 | bin = Contract._load_bin(__name__, 'abi/GemMock.bin')
30 |
31 | def __init__(self, web3: Web3, address: Address):
32 | assert(isinstance(web3, Web3))
33 | assert(isinstance(address, Address))
34 |
35 | self.web3 = web3
36 | self.address = address
37 | self._contract = self._get_contract(web3, self.abi, address)
38 |
39 | @staticmethod
40 | def deploy(web3: Web3, vat: Address, ilk: Ilk, gem: Address):
41 | assert isinstance(web3, Web3)
42 | assert isinstance(vat, Address)
43 | assert isinstance(ilk, Ilk)
44 | assert isinstance(gem, Address)
45 |
46 | return GemMock(web3=web3, address=Contract._deploy(web3, GemMock.abi, GemMock.bin, [vat.address,
47 | ilk.toBytes(),
48 | gem.address]))
49 | def ilk(self):
50 | return Ilk.fromBytes(self._contract.functions.ilk().call())
51 |
52 | def join(self, urn: Urn, value: Wad) -> Transact:
53 | assert(isinstance(urn, Urn))
54 | assert(isinstance(value, Wad))
55 |
56 | return Transact(self, self.web3, self.abi, self.address, self._contract,
57 | 'join', [urn.toBytes() , value.value])
58 |
59 | def hope(self, guy: Address) -> Transact:
60 | assert(isinstance(guy, Address))
61 |
62 | return Transact(self, self.web3, self.abi, self.address, self._contract,
63 | 'hope', [guy.address])
64 |
65 | def __eq__(self, other):
66 | return self.address == other.address
67 |
68 | def __repr__(self):
69 | return f"GemMock('{self.address}')"
70 |
--------------------------------------------------------------------------------
/pymaker/abi/DSVault.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[{"name":"token_","type":"address"}],"name":"swap","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint128"}],"name":"push","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint128"}],"name":"push","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"}],"name":"pull","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"src","type":"address"}],"name":"pull","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"mint","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"src","type":"address"},{"name":"wad","type":"uint128"}],"name":"pull","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"wad","type":"uint128"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"wad","type":"uint128"}],"name":"pull","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"}],"name":"push","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint128"}],"name":"burn","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"dst","type":"address"}],"name":"push","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"wad","type":"uint128"}],"name":"mint","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/pymaker/abi/DSRoles.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"getUserRoles","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"code","type":"address"},{"name":"sig","type":"bytes4"}],"name":"getCapabilityRoles","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"code","type":"address"},{"name":"sig","type":"bytes4"}],"name":"isCapabilityPublic","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"who","type":"address"},{"name":"role","type":"uint8"},{"name":"enabled","type":"bool"}],"name":"setUserRole","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"role","type":"uint8"},{"name":"code","type":"address"},{"name":"sig","type":"bytes4"},{"name":"enabled","type":"bool"}],"name":"setRoleCapability","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"},{"name":"role","type":"uint8"}],"name":"hasUserRole","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"caller","type":"address"},{"name":"code","type":"address"},{"name":"sig","type":"bytes4"}],"name":"canCall","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"code","type":"address"},{"name":"sig","type":"bytes4"},{"name":"enabled","type":"bool"}],"name":"setPublicCapability","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"who","type":"address"},{"name":"enabled","type":"bool"}],"name":"setRootUser","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"isUserRoot","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/pymaker/vault.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | from web3 import Web3
19 |
20 | from pymaker import Contract, Address, Transact
21 |
22 |
23 | class DSVault(Contract):
24 | """A client for the `DSVault` contract.
25 |
26 | You can find the source code of the `DSVault` contract here:
27 | .
28 |
29 | Attributes:
30 | web3: An instance of `Web` from `web3.py`.
31 | address: Ethereum address of the `DSVault` contract.
32 | """
33 |
34 | abi = Contract._load_abi(__name__, 'abi/DSVault.abi')
35 | bin = Contract._load_bin(__name__, 'abi/DSVault.bin')
36 |
37 | def __init__(self, web3: Web3, address: Address):
38 | assert(isinstance(web3, Web3))
39 | assert(isinstance(address, Address))
40 |
41 | self.web3 = web3
42 | self.address = address
43 | self._contract = self._get_contract(web3, self.abi, address)
44 |
45 | @staticmethod
46 | def deploy(web3: Web3):
47 | """Deploy a new instance of the `DSVault` contract.
48 |
49 | Args:
50 | web3: An instance of `Web` from `web3.py`.
51 |
52 | Returns:
53 | A `DSVault` class instance.
54 | """
55 | return DSVault(web3=web3, address=Contract._deploy(web3, DSVault.abi, DSVault.bin, []))
56 |
57 | def authority(self) -> Address:
58 | """Return the current `authority` of a `DSAuth`-ed contract.
59 |
60 | Returns:
61 | The address of the current `authority`.
62 | """
63 | return Address(self._contract.functions.authority().call())
64 |
65 | def set_authority(self, address: Address) -> Transact:
66 | """Set the `authority` of a `DSAuth`-ed contract.
67 |
68 | Args:
69 | address: The address of the new `authority`.
70 |
71 | Returns:
72 | A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.
73 | """
74 | assert(isinstance(address, Address))
75 | return Transact(self, self.web3, self.abi, self.address, self._contract, 'setAuthority', [address.address])
76 |
77 | def __repr__(self):
78 | return f"DSVault('{self.address}')"
79 |
--------------------------------------------------------------------------------
/pymaker/oracles.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2019 grandizzy
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | from web3 import Web3
19 |
20 | from pymaker import Contract, Address, Transact
21 | from pymaker.numeric import Wad
22 |
23 |
24 | # TODO: Complete implementation and unit test
25 | class OSM(Contract):
26 | """A client for the `OSM` contract.
27 |
28 | You can find the source code of the `OSM` contract here:
29 | .
30 |
31 | Attributes:
32 | web3: An instance of `Web` from `web3.py`.
33 | address: Ethereum address of the `OSM` contract.
34 | """
35 |
36 | abi = Contract._load_abi(__name__, 'abi/OSM.abi')
37 | bin = Contract._load_bin(__name__, 'abi/OSM.bin')
38 |
39 | def __init__(self, web3: Web3, address: Address):
40 | assert (isinstance(web3, Web3))
41 | assert (isinstance(address, Address))
42 |
43 | self.web3 = web3
44 | self.address = address
45 | self._contract = self._get_contract(web3, self.abi, address)
46 |
47 | def poke(self) -> Transact:
48 | return Transact(self, self.web3, self.abi, self.address, self._contract, 'poke', [])
49 |
50 | def peek(self) -> Wad:
51 | return Wad(self._extract_price(3))
52 |
53 | def peep(self) -> Wad:
54 | return Wad(self._extract_price(4))
55 |
56 | def zzz(self) -> int:
57 | return self._contract.functions.zzz().call()
58 |
59 | def _extract_price(self, storage_slot: int) -> int:
60 | assert isinstance(storage_slot, int)
61 | return Web3.toInt(self.web3.eth.getStorageAt(self.address.address, storage_slot)[16:])
62 |
63 | def __repr__(self):
64 | return f"OSM('{self.address}')"
65 |
66 |
67 | class OldUniv2LpOSM(OSM):
68 | """A custom `OSM` contract for Uniswap LP tokens which used different storage slots, obsolete as of dss-1.7.0
69 |
70 | You can find the source code of the `OSM` contract here:
71 | .
72 | """
73 |
74 | def __init__(self, web3: Web3, address: Address):
75 | super().__init__(web3, address)
76 |
77 | def peek(self) -> Wad:
78 | return Wad(self._extract_price(6))
79 |
80 | def peep(self) -> Wad:
81 | return Wad(self._extract_price(7))
82 |
--------------------------------------------------------------------------------
/pymaker/collateral.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2019-2021 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import logging
19 |
20 | from pymaker import Address, Contract
21 | from pymaker.approval import directly, hope_directly
22 | from pymaker.auctions import AuctionContract, Clipper, Flipper
23 | from pymaker.ilk import Ilk
24 | from pymaker.gas import DefaultGasPrice
25 | from pymaker.join import GemJoin
26 | from pymaker.token import DSToken, ERC20Token
27 |
28 |
29 | logger = logging.getLogger()
30 |
31 |
32 | class Collateral:
33 | """The `Collateral` object wraps accounting information in the Ilk with token-wide artifacts shared across
34 | multiple collateral types for the same token. For example, ETH-A and ETH-B are represented by different Ilks,
35 | but will share the same gem (WETH token), GemJoin instance, and Flipper contract.
36 | """
37 |
38 | def __init__(self, ilk: Ilk, gem: ERC20Token, adapter: GemJoin, auction: AuctionContract, pip, vat: Contract):
39 | assert isinstance(ilk, Ilk)
40 | assert isinstance(gem, ERC20Token)
41 | assert isinstance(adapter, GemJoin)
42 | assert isinstance(auction, AuctionContract)
43 | assert isinstance(vat, Contract)
44 |
45 | self.ilk = ilk
46 | self.gem = gem
47 | self.adapter = adapter
48 | if isinstance(auction, Flipper):
49 | self.flipper = auction
50 | self.clipper = None
51 | elif isinstance(auction, Clipper):
52 | self.flipper = None
53 | self.clipper = auction
54 | # Points to `median` for official deployments, `DSValue` for testing purposes.
55 | # Users generally have no need to interact with the pip.
56 | self.pip = pip
57 | self.vat = vat
58 |
59 | def approve(self, usr: Address, **kwargs):
60 | """
61 | Allows the user to move this collateral into and out of their CDP.
62 |
63 | Args
64 | usr: User making transactions with this collateral
65 | """
66 | gas_price = kwargs['gas_price'] if 'gas_price' in kwargs else DefaultGasPrice()
67 | self.adapter.approve(hope_directly(from_address=usr, gas_price=gas_price), self.vat.address)
68 | self.adapter.approve_token(directly(from_address=usr, gas_price=gas_price))
69 |
--------------------------------------------------------------------------------
/pymaker/abi/Jug.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"constant":true,"inputs":[],"name":"base","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"}],"name":"drip","outputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"},{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"address","name":"data","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"ilks","outputs":[{"internalType":"uint256","name":"duty","type":"uint256"},{"internalType":"uint256","name":"rho","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"}],"name":"init","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"vow","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/pymaker/sign.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import logging
19 | import time
20 | from typing import Tuple
21 |
22 | from eth_account.messages import defunct_hash_message
23 | from eth_utils import encode_hex
24 | from web3 import Web3
25 |
26 | from pymaker import Address
27 | from pymaker.keys import _registered_accounts
28 | from pymaker.util import bytes_to_hexstring
29 |
30 |
31 | def eth_sign(message: bytes, web3: Web3, key=None, in_hexbytes=False, account=None):
32 | assert(isinstance(message, bytes))
33 | assert(isinstance(web3, Web3))
34 |
35 | local_account = _registered_accounts.get((web3, Address(web3.eth.defaultAccount)))
36 |
37 | if local_account or (account is not None):
38 |
39 | if key is None:
40 | pkey = local_account.privateKey
41 | else:
42 | pkey = key
43 |
44 | start_time = time.time()
45 | start_clock = time.process_time()
46 | try:
47 | if in_hexbytes:
48 | message_hash = message
49 | else:
50 | message_hash = defunct_hash_message(primitive=message)
51 | signature = web3.eth.account.signHash(message_hash, private_key=pkey).signature.hex()
52 | finally:
53 | end_time = time.time()
54 | end_clock = time.process_time()
55 |
56 | logging.debug(f"Local signing took {end_time - start_time:.3f}s time, {end_clock - start_clock:.3f}s clock")
57 |
58 | return signature
59 |
60 | else:
61 | signature = bytes_to_hexstring(web3.manager.request_blocking(
62 | "eth_sign", [web3.eth.defaultAccount, encode_hex(message)],
63 | ))
64 |
65 | # for `EthereumJS TestRPC/v2.2.1/ethereum-js`
66 | if signature.endswith("00"):
67 | signature = signature[:-2] + "1b"
68 |
69 | if signature.endswith("01"):
70 | signature = signature[:-2] + "1c"
71 |
72 | return signature
73 |
74 |
75 | def to_vrs(signature: str) -> Tuple[int, bytes, bytes]:
76 | assert(isinstance(signature, str))
77 | assert(signature.startswith("0x"))
78 |
79 | signature_hex = signature[2:]
80 | r = bytes.fromhex(signature_hex[0:64])
81 | s = bytes.fromhex(signature_hex[64:128])
82 | v = ord(bytes.fromhex(signature_hex[128:130]))
83 |
84 | return v, r, s
85 |
--------------------------------------------------------------------------------
/pymaker/model.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2020 mitakash, MikeHathaway
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | from pprint import pformat
19 | from typing import Optional, List
20 |
21 | from pymaker import Address
22 | from pymaker.numeric import Wad
23 |
24 |
25 | class Token:
26 | def __init__(self, name: str, address: Optional[Address], decimals: int):
27 | assert(isinstance(name, str))
28 | assert(isinstance(address, Address) or (address is None))
29 | assert(isinstance(decimals, int))
30 |
31 | self.name = name
32 | self.address = address
33 | self.decimals = decimals
34 |
35 | self.min_amount = Wad.from_number(10 ** -self.decimals)
36 |
37 | def normalize_amount(self, amount: Wad) -> Wad:
38 | assert(isinstance(amount, Wad))
39 |
40 | return amount * Wad.from_number(10 ** (18 - self.decimals))
41 |
42 | def unnormalize_amount(self, amount: Wad) -> Wad:
43 | assert(isinstance(amount, Wad))
44 |
45 | return amount * Wad.from_number(10 ** (self.decimals - 18))
46 |
47 | def is_eth(self) -> bool:
48 | return self.address == Address('0x0000000000000000000000000000000000000000')
49 |
50 | def __eq__(self, other):
51 | assert(isinstance(other, Token))
52 | return self.name == other.name and \
53 | self.address == other.address and \
54 | self.decimals == other.decimals
55 |
56 | def __hash__(self):
57 | return hash((self.name, self.address, self.decimals))
58 |
59 | def __str__(self):
60 | return self.name
61 |
62 | def __repr__(self):
63 | return pformat(vars(self))
64 |
65 |
66 | class TokenConfig:
67 | def __init__(self, data: dict):
68 | assert (isinstance(data, dict))
69 |
70 | self.token_list = []
71 | self.token_config = data['tokens']
72 |
73 | def set_token_list(self, data):
74 | assert (isinstance(data, dict))
75 |
76 | self.token_list = [Token(name=key,
77 | address=Address(value['tokenAddress']) if 'tokenAddress' in value else None,
78 | decimals=value['tokenDecimals'] if 'tokenDecimals' in value else 18) for key, value in
79 | data['tokens'].items()]
80 |
81 | def get_token_list(self) -> List[Token]:
82 | return self.token_list
83 |
84 | def __repr__(self):
85 | return pformat(vars(self))
86 |
--------------------------------------------------------------------------------
/pymaker/abi/Spotter.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"ilk","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"val","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"spot","type":"uint256"}],"name":"Poke","type":"event"},{"constant":false,"inputs":[],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"},{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"},{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"address","name":"pip_","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"ilks","outputs":[{"internalType":"contract PipLike","name":"pip","type":"address"},{"internalType":"uint256","name":"mat","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"live","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"par","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"}],"name":"poke","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/pymaker/keys.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import getpass
19 | from typing import Optional
20 |
21 | from eth_account import Account
22 | from web3 import Web3
23 | from web3.middleware import construct_sign_and_send_raw_middleware
24 |
25 | from pymaker import Address
26 |
27 | _registered_accounts = {}
28 |
29 |
30 | def register_keys(web3: Web3, keys: Optional[list]):
31 | for key in keys or []:
32 | register_key(web3, key)
33 |
34 |
35 | def register_key(web3: Web3, key: str):
36 | assert(isinstance(web3, Web3))
37 |
38 | parsed = {}
39 | for p in key.split(","):
40 | var, val = p.split("=")
41 | parsed[var] = val
42 |
43 | register_key_file(web3, parsed.get('key_file'), parsed.get('pass_file', None))
44 |
45 |
46 | def register_key_file(web3: Web3, key_file: str, pass_file: Optional[str] = None):
47 | assert(isinstance(web3, Web3))
48 | assert(isinstance(key_file, str))
49 | assert(isinstance(pass_file, str) or (pass_file is None))
50 |
51 | with open(key_file) as key_file_open:
52 | read_key = key_file_open.read()
53 | if pass_file:
54 | with open(pass_file) as pass_file_open:
55 | read_pass = pass_file_open.read().replace("\n", "")
56 | else:
57 | read_pass = getpass.getpass(prompt=f"Password for {key_file}: ")
58 |
59 | private_key = Account.decrypt(read_key, read_pass)
60 | register_private_key(web3, private_key)
61 |
62 | def get_private_key(web3: Web3, key: str):
63 | assert(isinstance(web3, Web3))
64 | assert(isinstance(key, str))
65 |
66 | parsed = {}
67 | for p in key.split(","):
68 | var, val = p.split("=")
69 | parsed[var] = val
70 |
71 | with open(parsed.get('key_file')) as key_file_open:
72 | read_key = key_file_open.read()
73 | private_key = web3
74 | if parsed.get('pass_file'):
75 | with open(parsed.get('pass_file')) as pass_file_open:
76 | read_pass = pass_file_open.read().replace("\n", "")
77 | else:
78 | read_pass = getpass.getpass(prompt=f"Password for {key_file}: ")
79 |
80 | private_key = Account.decrypt(read_key, read_pass).hex()
81 | return private_key
82 |
83 | def register_private_key(web3: Web3, private_key):
84 | assert(isinstance(web3, Web3))
85 |
86 | account = Account.privateKeyToAccount(private_key)
87 |
88 | _registered_accounts[(web3, Address(account.address))] = account
89 | web3.middleware_onion.add(construct_sign_and_send_raw_middleware(account))
90 |
--------------------------------------------------------------------------------
/pymaker/abi/SaiTop.abi:
--------------------------------------------------------------------------------
1 | [{"constant":true,"inputs":[],"name":"sin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"skr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"era","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"flow","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tub","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"cooldown_","type":"uint256"}],"name":"setCooldown","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vox","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"cooldown","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"gem","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"sai","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fix","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"caged","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tap","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"tub_","type":"address"},{"name":"tap_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/tests/manual_test_goerli.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2020-2021 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import logging
19 | import os
20 | import sys
21 | from web3 import Web3, HTTPProvider
22 |
23 | from pymaker import Address, eth_transfer, web3_via_http
24 | from pymaker.gas import GeometricGasPrice
25 | from pymaker.lifecycle import Lifecycle
26 | from pymaker.keys import register_keys
27 | from pymaker.numeric import Wad
28 | from pymaker.token import EthToken
29 |
30 | logging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)
31 | # reduce logspew
32 | logging.getLogger('urllib3').setLevel(logging.INFO)
33 | logging.getLogger("web3").setLevel(logging.INFO)
34 | logging.getLogger("asyncio").setLevel(logging.INFO)
35 | logging.getLogger("requests").setLevel(logging.INFO)
36 |
37 | endpoint_uri = sys.argv[1]
38 | web3 = web3_via_http(endpoint_uri, timeout=10)
39 | print(web3.clientVersion)
40 |
41 | """
42 | Argument: Reqd? Example:
43 | Ethereum node URI yes https://localhost:8545
44 | Ethereum address no 0x0000000000000000000000000000000aBcdef123
45 | Private key no key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
46 | Gas price (GWEI) no 9
47 | """
48 |
49 |
50 | if len(sys.argv) > 3:
51 | web3.eth.defaultAccount = sys.argv[2]
52 | register_keys(web3, [sys.argv[3]])
53 | our_address = Address(web3.eth.defaultAccount)
54 | run_transactions = True
55 | elif len(sys.argv) > 2:
56 | our_address = Address(sys.argv[2])
57 | run_transactions = False
58 | else:
59 | our_address = None
60 | run_transactions = False
61 |
62 | gas_price = None if len(sys.argv) <= 4 else \
63 | GeometricGasPrice(initial_price=int(float(sys.argv[4]) * GeometricGasPrice.GWEI),
64 | every_secs=15,
65 | max_price=100 * GeometricGasPrice.GWEI)
66 |
67 | eth = EthToken(web3, Address.zero())
68 |
69 |
70 | class TestApp:
71 | def main(self):
72 | with Lifecycle(web3) as lifecycle:
73 | lifecycle.on_block(self.on_block)
74 |
75 | def on_block(self):
76 | block = web3.eth.blockNumber
77 | logging.info(f"Found block; web3.eth.blockNumber={block}")
78 |
79 | if run_transactions and block % 3 == 0:
80 | # dummy transaction: send 0 ETH to ourself
81 | eth_transfer(web3=web3, to=our_address, amount=Wad(0)).transact(
82 | from_address=our_address, gas=21000, gas_price=gas_price)
83 |
84 | if our_address:
85 | logging.info(f"Eth balance is {eth.balance_of(our_address)}")
86 |
87 |
88 | if __name__ == '__main__':
89 | TestApp().main()
90 |
--------------------------------------------------------------------------------
/pymaker/ilk.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2019-2021 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | from typing import Optional
19 | from web3 import Web3
20 |
21 | from pymaker.numeric import Wad, Ray, Rad
22 |
23 |
24 | class Ilk:
25 | """Models one collateral type, the combination of a token and a set of risk parameters.
26 | For example, ETH-A and ETH-B are different collateral types with the same underlying token (WETH) but with
27 | different risk parameters.
28 | """
29 |
30 | def __init__(self, name: str, rate: Optional[Ray] = None,
31 | ink: Optional[Wad] = None,
32 | art: Optional[Wad] = None,
33 | spot: Optional[Ray] = None,
34 | line: Optional[Rad] = None,
35 | dust: Optional[Rad] = None):
36 | assert (isinstance(name, str))
37 | assert (isinstance(rate, Ray) or (rate is None))
38 | assert (isinstance(ink, Wad) or (ink is None))
39 | assert (isinstance(art, Wad) or (art is None))
40 | assert (isinstance(spot, Ray) or (spot is None))
41 | assert (isinstance(line, Rad) or (line is None))
42 | assert (isinstance(dust, Rad) or (dust is None))
43 |
44 | self.name = name
45 | self.rate = rate
46 | self.ink = ink
47 | self.art = art
48 | self.spot = spot
49 | self.line = line
50 | self.dust = dust
51 |
52 | def toBytes(self):
53 | return Web3.toBytes(text=self.name).ljust(32, bytes(1))
54 |
55 | @staticmethod
56 | def fromBytes(ilk: bytes):
57 | assert (isinstance(ilk, bytes))
58 |
59 | name = Web3.toText(ilk.strip(bytes(1)))
60 | return Ilk(name)
61 |
62 | def __eq__(self, other):
63 | assert isinstance(other, Ilk)
64 |
65 | return (self.name == other.name) \
66 | and (self.rate == other.rate) \
67 | and (self.ink == other.ink) \
68 | and (self.art == other.art) \
69 | and (self.spot == other.spot) \
70 | and (self.line == other.line) \
71 | and (self.dust == other.dust)
72 |
73 | def __repr__(self):
74 | repr = ''
75 | if self.rate:
76 | repr += f' rate={self.rate}'
77 | if self.ink:
78 | repr += f' Ink={self.ink}'
79 | if self.art:
80 | repr += f' Art={self.art}'
81 | if self.spot:
82 | repr += f' spot={self.spot}'
83 | if self.line:
84 | repr += f' line={self.line}'
85 | if self.dust:
86 | repr += f' dust={self.dust}'
87 | if repr:
88 | repr = f'[{repr.strip()}]'
89 |
90 | return f"Ilk('{self.name}'){repr}"
91 |
--------------------------------------------------------------------------------
/pymaker/abi/Pot.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"constant":true,"inputs":[],"name":"Pie","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"chi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"drip","outputs":[{"internalType":"uint256","name":"tmp","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"dsr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"address","name":"addr","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"join","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"live","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pie","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rho","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"vow","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/tests/manual_test_tx_recovery.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2020 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import asyncio
19 | import logging
20 | import os
21 | import sys
22 | import time
23 | import threading
24 | from pprint import pprint
25 |
26 | from pymaker import Address, get_pending_transactions, Wad, web3_via_http
27 | from pymaker.deployment import DssDeployment
28 | from pymaker.gas import FixedGasPrice, GeometricGasPrice
29 | from pymaker.keys import register_keys
30 |
31 | logging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)
32 | # reduce logspew
33 | logging.getLogger('urllib3').setLevel(logging.INFO)
34 | logging.getLogger("web3").setLevel(logging.INFO)
35 | logging.getLogger("asyncio").setLevel(logging.INFO)
36 | logging.getLogger("requests").setLevel(logging.INFO)
37 |
38 | web3 = web3_via_http(endpoint_uri=os.environ['ETH_RPC_URL'])
39 | web3.eth.defaultAccount = sys.argv[1] # ex: 0x0000000000000000000000000000000aBcdef123
40 | register_keys(web3, [sys.argv[2]]) # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
41 | our_address = Address(web3.eth.defaultAccount)
42 | weth = DssDeployment.from_node(web3).collaterals['ETH-A'].gem
43 | stuck_txes_to_submit = int(sys.argv[3]) if len(sys.argv) > 3 else 1
44 |
45 | GWEI = 1000000000
46 | increasing_gas = GeometricGasPrice(initial_price=int(1 * GWEI), every_secs=30, coefficient=1.5, max_price=100 * GWEI)
47 |
48 |
49 | class TestApp:
50 | def main(self):
51 | self.startup()
52 |
53 | pending_txes = get_pending_transactions(web3)
54 | pprint(list(map(lambda t: f"{t.name()} with gas {t.current_gas}", pending_txes)))
55 |
56 | if len(pending_txes) > 0:
57 | while len(pending_txes) > 0:
58 | pending_txes[0].cancel(gas_price=increasing_gas)
59 | # After the synchronous cancel, wait to see if subsequent transactions get mined
60 | time.sleep(15)
61 | pending_txes = get_pending_transactions(web3)
62 | else:
63 | logging.info(f"No pending transactions were found; submitting {stuck_txes_to_submit}")
64 | for i in range(1, stuck_txes_to_submit+1):
65 | self._run_future(weth.deposit(Wad(i)).transact_async(gas_price=FixedGasPrice(int(0.4 * i * GWEI))))
66 | time.sleep(2)
67 |
68 | self.shutdown()
69 |
70 | def startup(self):
71 | pass
72 |
73 | def shutdown(self):
74 | pass
75 |
76 | @staticmethod
77 | def _run_future(future):
78 | def worker():
79 | loop = asyncio.new_event_loop()
80 | asyncio.set_event_loop(loop)
81 | try:
82 | asyncio.get_event_loop().run_until_complete(future)
83 | finally:
84 | loop.close()
85 |
86 | thread = threading.Thread(target=worker, daemon=True)
87 | thread.start()
88 |
89 |
90 | if __name__ == '__main__':
91 | TestApp().main()
92 |
--------------------------------------------------------------------------------
/pymaker/abi/DSAuth.bin:
--------------------------------------------------------------------------------
1 | 608060405234801561001057600080fd5b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a26106cd806100a46000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806313af4035146100515780637a9e5e4b146100955780638da5cb5b146100d9578063bf7e214f14610123575b600080fd5b6100936004803603602081101561006757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061016d565b005b6100d7600480360360208110156100ab57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102b6565b005b6100e16103fd565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61012b610423565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61019b336000357fffffffff0000000000000000000000000000000000000000000000000000000016610448565b61020d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f64732d617574682d756e617574686f72697a656400000000000000000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b6102e4336000357fffffffff0000000000000000000000000000000000000000000000000000000016610448565b610356576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f64732d617574682d756e617574686f72697a656400000000000000000000000081525060200191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610487576001905061069b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156104e6576001905061069b565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610545576000905061069b565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001935050505060206040518083038186803b15801561065d57600080fd5b505afa158015610671573d6000803e3d6000fd5b505050506040513d602081101561068757600080fd5b810190808051906020019092919050505090505b9291505056fea165627a7a72305820185e07675ea15995cd10a2394a35492441f7f0c23183422fa7b0f937153825ea0029
--------------------------------------------------------------------------------
/tests/manual_test_node.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2020 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import logging
19 | import os
20 | import sys
21 | from web3 import Web3, HTTPProvider
22 |
23 | from pymaker import Address
24 | from pymaker.lifecycle import Lifecycle
25 | from pymaker.deployment import DssDeployment
26 | from pymaker.keys import register_keys
27 | from pymaker.numeric import Wad
28 |
29 | logging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)
30 | # reduce logspew
31 | logging.getLogger('urllib3').setLevel(logging.INFO)
32 | logging.getLogger("web3").setLevel(logging.INFO)
33 | logging.getLogger("asyncio").setLevel(logging.INFO)
34 | logging.getLogger("requests").setLevel(logging.INFO)
35 |
36 | endpoint_uri = sys.argv[1] # ex: https://localhost:8545
37 | web3 = Web3(HTTPProvider(endpoint_uri=endpoint_uri, request_kwargs={"timeout": 30}))
38 | if len(sys.argv) > 3:
39 | web3.eth.defaultAccount = sys.argv[2] # ex: 0x0000000000000000000000000000000aBcdef123
40 | register_keys(web3, [sys.argv[3]]) # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
41 | our_address = Address(web3.eth.defaultAccount)
42 | run_transactions = True
43 | elif len(sys.argv) > 2:
44 | our_address = Address(sys.argv[2])
45 | run_transactions = False
46 | else:
47 | our_address = None
48 | run_transactions = False
49 |
50 | mcd = DssDeployment.from_node(web3)
51 | collateral = mcd.collaterals['ETH-A']
52 | ilk = collateral.ilk
53 | if run_transactions:
54 | collateral.approve(our_address)
55 | past_blocks = 100
56 |
57 |
58 | class TestApp:
59 | def __init__(self):
60 | self.amount = Wad(3)
61 | self.joined = Wad(0)
62 |
63 | def main(self):
64 | with Lifecycle(web3) as lifecycle:
65 | lifecycle.on_shutdown(self.on_shutdown)
66 | lifecycle.on_block(self.on_block)
67 |
68 | def on_block(self):
69 | if run_transactions:
70 | logging.info(f"Found block {web3.eth.blockNumber}, joining {self.amount} {ilk.name} to our urn")
71 | collateral.gem.deposit(self.amount).transact()
72 | assert collateral.adapter.join(our_address, self.amount).transact()
73 | self.joined += self.amount
74 | else:
75 | logging.info(f"Found block; web3.eth.blockNumber={web3.eth.blockNumber}")
76 | if our_address:
77 | logging.info(f"Urn balance is {mcd.vat.gem(ilk, our_address)} {ilk.name}")
78 | # self.request_history()
79 |
80 | def request_history(self):
81 | logs = mcd.vat.past_frobs(web3.eth.blockNumber - past_blocks)
82 | logging.info(f"Found {len(logs)} frobs in the past {past_blocks} blocks")
83 |
84 | def on_shutdown(self):
85 | if run_transactions and self.joined > Wad(0):
86 | logging.info(f"Exiting {self.joined} {ilk.name} from our urn")
87 | assert collateral.adapter.exit(our_address, self.joined).transact()
88 | assert collateral.gem.withdraw(self.joined).transact()
89 |
90 |
91 | if __name__ == '__main__':
92 | TestApp().main()
93 |
--------------------------------------------------------------------------------
/pymaker/cdpmanager.py:
--------------------------------------------------------------------------------
1 |
2 | # This file is part of Maker Keeper Framework.
3 | #
4 | # Copyright (C) 2017-2020 ith-harvey
5 | #
6 | # This program is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU Affero General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # This program is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU Affero General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU Affero General Public License
17 | # along with this program. If not, see .
18 |
19 |
20 | from web3 import Web3
21 | from pymaker import Address, Contract, Transact
22 | from pymaker.dss import Ilk, Urn, Vat
23 | from pymaker.numeric import Wad
24 |
25 |
26 | class CdpManager(Contract):
27 | """A client for the `DSCdpManger` contract, which is a wrapper around the cdp system, for easier use.
28 |
29 | Ref.
30 | """
31 |
32 | abi = Contract._load_abi(__name__, 'abi/DssCdpManager.abi')
33 | bin = Contract._load_bin(__name__, 'abi/DssCdpManager.bin')
34 |
35 | def __init__(self, web3: Web3, address: Address):
36 | assert isinstance(web3, Web3)
37 | assert isinstance(address, Address)
38 |
39 | self.web3 = web3
40 | self.address = address
41 | self._contract = self._get_contract(web3, self.abi, address)
42 | self.vat = Vat(self.web3, Address(self._contract.functions.vat().call()))
43 |
44 | def open(self, ilk: Ilk, address: Address) -> Transact:
45 | assert isinstance(ilk, Ilk)
46 | assert isinstance(address, Address)
47 |
48 | return Transact(self, self.web3, self.abi, self.address, self._contract, 'open',
49 | [ilk.toBytes(), address.address])
50 |
51 | def urn(self, cdpid: int) -> Urn:
52 | '''Returns Urn for respective CDP ID'''
53 | assert isinstance(cdpid, int)
54 |
55 | urn_address = Address(self._contract.functions.urns(cdpid).call())
56 | ilk = self.ilk(cdpid)
57 | urn = self.vat.urn(ilk, Address(urn_address))
58 |
59 | return urn
60 |
61 | def owns(self, cdpid: int) -> Address:
62 | '''Returns owner Address of respective CDP ID'''
63 | assert isinstance(cdpid, int)
64 |
65 | owner = Address(self._contract.functions.owns(cdpid).call())
66 | return owner
67 |
68 | def ilk(self, cdpid: int) -> Ilk:
69 | '''Returns Ilk for respective CDP ID'''
70 | assert isinstance(cdpid, int)
71 |
72 | ilk = Ilk.fromBytes(self._contract.functions.ilks(cdpid).call())
73 | return ilk
74 |
75 | def first(self, address: Address) -> int:
76 | '''Returns first CDP Id created by owner address'''
77 | assert isinstance(address, Address)
78 |
79 | cdpid = int(self._contract.functions.first(address.address).call())
80 | return cdpid
81 |
82 | def last(self, address: Address) -> int:
83 | '''Returns last CDP Id created by owner address'''
84 | assert isinstance(address, Address)
85 |
86 | cdpid = self._contract.functions.last(address.address).call()
87 | return int(cdpid)
88 |
89 | def count(self, address: Address) -> int:
90 | '''Returns number of CDP's created using the DS-Cdp-Manager contract specifically'''
91 | assert isinstance(address, Address)
92 |
93 | count = int(self._contract.functions.count(address.address).call())
94 | return count
95 |
96 | def __repr__(self):
97 | return f"CdpManager('{self.address}')"
98 |
--------------------------------------------------------------------------------
/tests/test_savings.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2019 grandizzy
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import pytest
19 |
20 | from pymaker import Address
21 | from pymaker.deployment import DssDeployment
22 | from pymaker.dsr import Dsr
23 | from pymaker.numeric import Wad, Ray, Rad
24 |
25 | from tests.test_dss import wrap_eth, frob
26 |
27 |
28 | @pytest.fixture
29 | def dsr(our_address: Address, mcd: DssDeployment) -> Dsr:
30 | return Dsr(mcd, our_address)
31 |
32 |
33 | @pytest.mark.dependency()
34 | def test_proxy(dsr):
35 | assert dsr.has_proxy() is False
36 |
37 | dsr.build_proxy().transact()
38 | assert dsr.has_proxy() is True
39 |
40 |
41 | @pytest.mark.dependency(depends=['test_proxy'])
42 | def test_join_and_exit(dsr):
43 | proxy = dsr.get_proxy()
44 | assert dsr.get_balance(proxy.address) == Wad.from_number(0)
45 |
46 | mcd = dsr.mcd
47 |
48 | # create a vault
49 | collateral = mcd.collaterals['ETH-C']
50 | wrap_eth(mcd, dsr.owner, Wad.from_number(2))
51 | collateral.approve(dsr.owner)
52 | assert collateral.adapter.join(dsr.owner, Wad.from_number(2)).transact(from_address=dsr.owner)
53 | frob(mcd, collateral, dsr.owner, dink=Wad.from_number(2), dart=Wad(0))
54 | dart = Wad.from_number(100)
55 | frob(mcd, collateral, dsr.owner, dink=Wad(0), dart=dart)
56 |
57 | # mint and withdraw all the Dai
58 | mcd.approve_dai(dsr.owner)
59 | assert mcd.dai_adapter.exit(dsr.owner, dart).transact(from_address=dsr.owner)
60 | assert mcd.dai.balance_of(dsr.owner) == dart
61 |
62 | initial_dai_balance = mcd.dai.balance_of(dsr.owner)
63 | assert initial_dai_balance >= Wad.from_number(100)
64 | assert dsr.get_balance(proxy.address) == Wad.from_number(0)
65 |
66 | # approve Proxy to use 100 DAI from account
67 | mcd.dai.approve(proxy.address, Wad.from_number(100)).transact(from_address=dsr.owner)
68 |
69 | # join 100 DAI in DSR
70 | assert dsr.join(Wad.from_number(100), proxy).transact(from_address=dsr.owner)
71 | assert mcd.dai.balance_of(dsr.owner) == initial_dai_balance - Wad.from_number(100)
72 | assert round(dsr.get_balance(proxy.address)) == Wad.from_number(100)
73 | assert mcd.pot.drip().transact()
74 |
75 | # exit 33 DAI from DSR
76 | assert dsr.exit(Wad.from_number(33), proxy).transact(from_address=dsr.owner)
77 | assert round(mcd.dai.balance_of(dsr.owner)) == round(initial_dai_balance) - Wad.from_number(100) + Wad.from_number(33)
78 | assert round(dsr.get_balance(proxy.address)) == Wad.from_number(67)
79 | assert mcd.pot.drip().transact()
80 |
81 | # exit remaining DAI from DSR and join to vat
82 | assert dsr.exit_all(proxy).transact(from_address=dsr.owner)
83 | assert round(mcd.dai.balance_of(dsr.owner)) == round(initial_dai_balance)
84 | assert dsr.get_balance(proxy.address) == Wad.from_number(0)
85 | assert mcd.dai_adapter.join(dsr.owner, mcd.dai.balance_of(dsr.owner)).transact(from_address=dsr.owner)
86 |
87 | # repay the vault
88 | assert collateral.ilk.dust == Rad(0)
89 | wipe: Wad = mcd.vat.get_wipe_all_dart(collateral.ilk, dsr.owner)
90 | frob(mcd, collateral, dsr.owner, dink=Wad(0), dart=wipe*-1)
91 |
--------------------------------------------------------------------------------
/tests/test_feed.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import pytest
19 | from web3 import Web3, HTTPProvider
20 |
21 | from pymaker import Address
22 | from pymaker.feed import DSValue
23 |
24 |
25 | class TestDSValue:
26 | def setup_method(self):
27 | self.web3 = Web3(HTTPProvider("http://localhost:8555"))
28 | self.web3.eth.defaultAccount = self.web3.eth.accounts[0]
29 | self.dsvalue = DSValue.deploy(self.web3)
30 |
31 | def test_fail_when_no_contract_under_that_address(self):
32 | # expect
33 | with pytest.raises(Exception):
34 | DSValue(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))
35 |
36 | def test_address(self):
37 | assert isinstance(self.dsvalue.address, Address)
38 |
39 | def test_no_value_after_deploy(self):
40 | # expect
41 | assert self.dsvalue.has_value() is False
42 | with pytest.raises(Exception):
43 | self.dsvalue.read()
44 | with pytest.raises(Exception):
45 | self.dsvalue.read_as_int()
46 | with pytest.raises(Exception):
47 | self.dsvalue.read_as_hex()
48 |
49 | def test_poke(self):
50 | # when
51 | self.dsvalue.poke(bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4])).transact()
55 |
56 | # then
57 | assert self.dsvalue.has_value() is True
58 | assert self.dsvalue.read_as_int() == 500
59 | assert self.dsvalue.read() == bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
62 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4])
63 |
64 | def test_poke_with_int(self):
65 | # when
66 | self.dsvalue.poke_with_int(500).transact()
67 |
68 | # then
69 | assert self.dsvalue.has_value() is True
70 | assert self.dsvalue.read_as_int() == 500
71 | assert self.dsvalue.read() == bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4])
75 |
76 | def test_void(self):
77 | # given
78 | self.dsvalue.poke_with_int(250).transact()
79 | assert self.dsvalue.has_value() is True
80 |
81 | # when
82 | self.dsvalue.void().transact()
83 |
84 | # then
85 | assert self.dsvalue.has_value() is False
86 |
87 | def test_should_have_printable_representation(self):
88 | assert repr(self.dsvalue) == f"DSValue('{self.dsvalue.address}')"
89 |
--------------------------------------------------------------------------------
/pymaker/abi/ExchangeV2-ERC20Proxy.abi:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "constant": false,
4 | "inputs": [
5 | {
6 | "name": "target",
7 | "type": "address"
8 | }
9 | ],
10 | "name": "addAuthorizedAddress",
11 | "outputs": [],
12 | "payable": false,
13 | "stateMutability": "nonpayable",
14 | "type": "function"
15 | },
16 | {
17 | "constant": true,
18 | "inputs": [
19 | {
20 | "name": "",
21 | "type": "uint256"
22 | }
23 | ],
24 | "name": "authorities",
25 | "outputs": [
26 | {
27 | "name": "",
28 | "type": "address"
29 | }
30 | ],
31 | "payable": false,
32 | "stateMutability": "view",
33 | "type": "function"
34 | },
35 | {
36 | "constant": false,
37 | "inputs": [
38 | {
39 | "name": "target",
40 | "type": "address"
41 | }
42 | ],
43 | "name": "removeAuthorizedAddress",
44 | "outputs": [],
45 | "payable": false,
46 | "stateMutability": "nonpayable",
47 | "type": "function"
48 | },
49 | {
50 | "constant": true,
51 | "inputs": [],
52 | "name": "owner",
53 | "outputs": [
54 | {
55 | "name": "",
56 | "type": "address"
57 | }
58 | ],
59 | "payable": false,
60 | "stateMutability": "view",
61 | "type": "function"
62 | },
63 | {
64 | "constant": false,
65 | "inputs": [
66 | {
67 | "name": "target",
68 | "type": "address"
69 | },
70 | {
71 | "name": "index",
72 | "type": "uint256"
73 | }
74 | ],
75 | "name": "removeAuthorizedAddressAtIndex",
76 | "outputs": [],
77 | "payable": false,
78 | "stateMutability": "nonpayable",
79 | "type": "function"
80 | },
81 | {
82 | "constant": true,
83 | "inputs": [],
84 | "name": "getProxyId",
85 | "outputs": [
86 | {
87 | "name": "",
88 | "type": "bytes4"
89 | }
90 | ],
91 | "payable": false,
92 | "stateMutability": "pure",
93 | "type": "function"
94 | },
95 | {
96 | "constant": true,
97 | "inputs": [
98 | {
99 | "name": "",
100 | "type": "address"
101 | }
102 | ],
103 | "name": "authorized",
104 | "outputs": [
105 | {
106 | "name": "",
107 | "type": "bool"
108 | }
109 | ],
110 | "payable": false,
111 | "stateMutability": "view",
112 | "type": "function"
113 | },
114 | {
115 | "constant": true,
116 | "inputs": [],
117 | "name": "getAuthorizedAddresses",
118 | "outputs": [
119 | {
120 | "name": "",
121 | "type": "address[]"
122 | }
123 | ],
124 | "payable": false,
125 | "stateMutability": "view",
126 | "type": "function"
127 | },
128 | {
129 | "constant": false,
130 | "inputs": [
131 | {
132 | "name": "newOwner",
133 | "type": "address"
134 | }
135 | ],
136 | "name": "transferOwnership",
137 | "outputs": [],
138 | "payable": false,
139 | "stateMutability": "nonpayable",
140 | "type": "function"
141 | },
142 | {
143 | "payable": false,
144 | "stateMutability": "nonpayable",
145 | "type": "fallback"
146 | },
147 | {
148 | "anonymous": false,
149 | "inputs": [
150 | {
151 | "indexed": true,
152 | "name": "target",
153 | "type": "address"
154 | },
155 | {
156 | "indexed": true,
157 | "name": "caller",
158 | "type": "address"
159 | }
160 | ],
161 | "name": "AuthorizedAddressAdded",
162 | "type": "event"
163 | },
164 | {
165 | "anonymous": false,
166 | "inputs": [
167 | {
168 | "indexed": true,
169 | "name": "target",
170 | "type": "address"
171 | },
172 | {
173 | "indexed": true,
174 | "name": "caller",
175 | "type": "address"
176 | }
177 | ],
178 | "name": "AuthorizedAddressRemoved",
179 | "type": "event"
180 | }
181 | ]
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | pymaker API
2 | ===========
3 |
4 | The `pymaker` API exists to provide a simple way of interacting with Maker smart contracts.
5 |
6 | It was designed to simplify and facilitate creation of external profit-seeking agents, usually called keepers,
7 | that operate around the stablecoin set of smart contracts. The API can also be used to automate certain tasks for
8 | other entities involved in the platform, like DAI issuers or traders.
9 |
10 |
11 | General
12 | -------
13 |
14 | Address
15 | ~~~~~~~
16 |
17 | .. autoclass:: pymaker.Address
18 | :members:
19 |
20 | Transact
21 | ~~~~~~~~
22 |
23 | .. autoclass:: pymaker.Transact
24 | :members:
25 |
26 | Calldata
27 | ~~~~~~~~
28 |
29 | .. autoclass:: pymaker.Calldata
30 | :members:
31 |
32 | Invocation
33 | ~~~~~~~~~~
34 |
35 | .. autoclass:: pymaker.Invocation
36 | :members:
37 |
38 | Receipt
39 | ~~~~~~~
40 |
41 | .. autoclass:: pymaker.Receipt
42 | :members:
43 |
44 | Transfer
45 | ~~~~~~~~
46 |
47 | .. autoclass:: pymaker.Transfer
48 | :members:
49 |
50 |
51 | Numeric types
52 | -------------
53 |
54 | Most of the numeric data throughout the entire platform is kept as either `Wad` (18-digit precision type)
55 | or `Ray` (27-digit precision type).
56 |
57 | Wad
58 | ~~~
59 |
60 | .. autoclass:: pymaker.numeric.Wad
61 | :members:
62 |
63 | Ray
64 | ~~~
65 |
66 | .. autoclass:: pymaker.numeric.Ray
67 | :members:
68 |
69 |
70 | Gas price
71 | ---------
72 |
73 | .. autoclass:: pymaker.gas.GasPrice
74 | :members:
75 |
76 | The following implementations of `GasPrice` are available:
77 |
78 | DefaultGasPrice
79 | ~~~~~~~~~~~~~~~
80 |
81 | .. autoclass:: pymaker.gas.DefaultGasPrice
82 | :members:
83 |
84 | FixedGasPrice
85 | ~~~~~~~~~~~~~
86 |
87 | .. autoclass:: pymaker.gas.FixedGasPrice
88 | :members:
89 |
90 | IncreasingGasPrice
91 | ~~~~~~~~~~~~~~~~~~
92 |
93 | .. autoclass:: pymaker.gas.IncreasingGasPrice
94 | :members:
95 |
96 |
97 | Approvals
98 | ---------
99 |
100 | .. automodule:: pymaker.approval
101 | :members:
102 |
103 |
104 | Contracts
105 | ---------
106 |
107 | DAI Stablecoin
108 | ~~~~~~~~~~~~~~
109 |
110 | Tub
111 | """
112 |
113 | .. autoclass:: pymaker.sai.Tub
114 | :members:
115 |
116 | Tap
117 | """
118 |
119 | .. autoclass:: pymaker.sai.Tap
120 | :members:
121 |
122 | Top
123 | """
124 |
125 | .. autoclass:: pymaker.sai.Top
126 | :members:
127 |
128 | Vox
129 | """
130 |
131 | .. autoclass:: pymaker.sai.Vox
132 | :members:
133 |
134 | ERC20
135 | ~~~~~
136 |
137 | ERC20Token
138 | """"""""""
139 |
140 | .. autoclass:: pymaker.token.ERC20Token
141 | :members:
142 |
143 | DSToken
144 | """""""
145 |
146 | .. autoclass:: pymaker.token.DSToken
147 | :members:
148 |
149 | DSEthToken
150 | """"""""""
151 |
152 | .. autoclass:: pymaker.token.DSEthToken
153 | :members:
154 |
155 |
156 | Exchanges
157 | ~~~~~~~~~
158 |
159 | `OaaisDEX`, `EtherDelta` and `0x` are decentralized exchanges which also provide some arbitrage opportunities
160 | for profit-seeking agents. Because of that an API has been created around them as well. Also an API for
161 | the `Bibox` centralized exchange is present.
162 |
163 | OasisDEX
164 | """"""""
165 |
166 | .. automodule:: pymaker.oasis
167 | :members:
168 |
169 | EtherDelta
170 | """"""""""
171 |
172 | .. automodule:: pymaker.etherdelta
173 | :members:
174 |
175 | 0x
176 | ""
177 |
178 | .. automodule:: pymaker.zrx
179 | :members:
180 |
181 | Bibox
182 | """""
183 |
184 | .. automodule:: pymaker.bibox
185 | :members:
186 |
187 |
188 | Authentication
189 | ~~~~~~~~~~~~~~
190 |
191 | DSGuard
192 | """""""
193 |
194 | .. autoclass:: pymaker.auth.DSGuard
195 | :members:
196 |
197 |
198 | DSValue
199 | ~~~~~~~
200 |
201 | .. autoclass:: pymaker.feed.DSValue
202 | :members:
203 |
204 | DSVault
205 | ~~~~~~~
206 |
207 | .. autoclass:: pymaker.vault.DSVault
208 | :members:
209 |
210 |
211 |
212 | Atomic transactions
213 | -------------------
214 |
215 | TxManager
216 | ~~~~~~~~~
217 |
218 | .. autoclass:: pymaker.transactional.TxManager
219 | :members:
220 |
--------------------------------------------------------------------------------
/tests/test_auth.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import pytest
19 | from web3 import Web3, HTTPProvider
20 |
21 | from pymaker import Address
22 | from pymaker.auth import DSGuard, DSAuth
23 | from pymaker.util import hexstring_to_bytes
24 |
25 |
26 | class TestDSGuard:
27 | def setup_method(self):
28 | self.web3 = Web3(HTTPProvider("http://localhost:8555"))
29 | self.web3.eth.defaultAccount = self.web3.eth.accounts[0]
30 | self.our_address = Address(self.web3.eth.defaultAccount)
31 | self.ds_guard = DSGuard.deploy(self.web3)
32 |
33 | def can_call(self, src: str, dst: str, sig: str) -> bool:
34 | return self.ds_guard._contract.functions.canCall(src, dst, hexstring_to_bytes(sig)).call()
35 |
36 | def test_fail_when_no_contract_under_that_address(self):
37 | # expect
38 | with pytest.raises(Exception):
39 | DSGuard(web3=self.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))
40 |
41 | def test_no_permit_by_default(self):
42 | # expect
43 | assert not self.can_call(src='0x1111111111222222222211111111112222222222',
44 | dst='0x3333333333444444444433333333334444444444',
45 | sig='0xab121fd7')
46 |
47 | def test_permit_any_to_any_with_any_sig(self):
48 | # when
49 | self.ds_guard.permit(DSGuard.ANY, DSGuard.ANY, DSGuard.ANY).transact()
50 |
51 | # then
52 | assert self.can_call(src='0x1111111111222222222211111111112222222222',
53 | dst='0x3333333333444444444433333333334444444444',
54 | sig='0xab121fd7')
55 |
56 | def test_permit_specific_addresses_and_sig(self):
57 | # when
58 | self.ds_guard.permit(src=Address('0x1111111111222222222211111111112222222222'),
59 | dst=Address('0x3333333333444444444433333333334444444444'),
60 | sig=hexstring_to_bytes('0xab121fd7')).transact()
61 |
62 | # then
63 | assert self.can_call(src='0x1111111111222222222211111111112222222222',
64 | dst='0x3333333333444444444433333333334444444444',
65 | sig='0xab121fd7')
66 |
67 | # and
68 | assert not self.can_call(src='0x3333333333444444444433333333334444444444',
69 | dst='0x1111111111222222222211111111112222222222',
70 | sig='0xab121fd7') # different addresses
71 | assert not self.can_call(src='0x1111111111222222222211111111112222222222',
72 | dst='0x3333333333444444444433333333334444444444',
73 | sig='0xab121fd8') # different sig
74 |
75 |
76 | class TestDSAuth:
77 | def setup_method(self):
78 | self.web3 = Web3(HTTPProvider("http://localhost:8555"))
79 | self.web3.eth.defaultAccount = self.web3.eth.accounts[0]
80 | self.our_address = Address(self.web3.eth.defaultAccount)
81 |
82 | self.ds_auth = DSAuth.deploy(self.web3)
83 |
84 | @pytest.mark.skip(reason="calls to ABI/BIN are not working on ganache")
85 | def test_owner(self):
86 | owner = self.ds_auth.get_owner()
87 | assert isinstance(owner, Address)
88 | assert owner == self.web3.eth.accounts[0]
89 |
90 | assert self.ds_auth.set_owner(self.web3.eth.accounts[1])
91 | assert self.ds_auth.get_owner() == self.web3.eth.accounts[1]
92 |
--------------------------------------------------------------------------------
/pymaker/dsrmanager.py:
--------------------------------------------------------------------------------
1 |
2 | # This file is part of Maker Keeper Framework.
3 | #
4 | # Copyright (C) 2020 Maker Ecosystem Growth Holdings, INC
5 | #
6 | # This program is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU Affero General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # This program is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU Affero General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU Affero General Public License
17 | # along with this program. If not, see .
18 |
19 |
20 | from web3 import Web3
21 | from pymaker import Address, Contract, Transact
22 | from pymaker.dss import Pot
23 | from pymaker.join import DaiJoin
24 | from pymaker.numeric import Wad, Rad
25 | from pymaker.token import DSToken
26 |
27 |
28 | class DsrManager(Contract):
29 | """
30 | A client for the `DsrManger` contract, which reduces the need for proxies
31 | when interacting with the Pot contract.
32 |
33 | Ref.
34 | """
35 |
36 | abi = Contract._load_abi(__name__, 'abi/DsrManager.abi')
37 | bin = Contract._load_bin(__name__, 'abi/DsrManager.bin')
38 |
39 | def __init__(self, web3: Web3, address: Address):
40 | assert isinstance(web3, Web3)
41 | assert isinstance(address, Address)
42 |
43 | self.web3 = web3
44 | self.address = address
45 | self._contract = self._get_contract(web3, self.abi, address)
46 |
47 | def pot(self) -> Pot:
48 | address = Address(self._contract.functions.pot().call())
49 | return Pot(self.web3, address)
50 |
51 | def dai(self) -> DSToken:
52 | address = Address(self._contract.functions.dai().call())
53 | return DSToken(self.web3, address)
54 |
55 | def dai_adapter(self) -> DaiJoin:
56 | address = Address(self._contract.functions.daiJoin().call())
57 | return DaiJoin(self.web3, address)
58 |
59 | def supply(self) -> Wad:
60 | """Total supply of pie locked in Pot through DsrManager"""
61 | return Wad(self._contract.functions.supply().call())
62 |
63 | def pie_of(self, usr: Address) -> Wad:
64 | """Pie balance of a given usr address"""
65 | assert isinstance(usr, Address)
66 |
67 | return Wad(self._contract.functions.pieOf(usr.address).call())
68 |
69 | def dai_of(self, usr: Address) -> Rad:
70 | """
71 | Internal Dai balance of a given usr address - current Chi is used
72 | i.e. Dai balance potentially stale
73 | """
74 | assert isinstance(usr, Address)
75 |
76 | pie = self.pie_of(usr)
77 | chi = self.pot().chi()
78 |
79 | dai = Rad(pie) * Rad(chi)
80 |
81 | return dai
82 |
83 | def join(self, dst: Address, dai: Wad) -> Transact:
84 | """Lock a given amount of ERC20 Dai into the DSR Contract and give to dst address """
85 | assert isinstance(dst, Address)
86 | assert isinstance(dai, Wad)
87 |
88 | return Transact(self, self.web3, self.abi, self.address, self._contract, 'join',
89 | [dst.address, dai.value])
90 |
91 | def exit(self, dst: Address, dai: Wad) -> Transact:
92 | """ Free a given amount of ERC20 Dai from the DSR Contract and give to dst address """
93 | assert isinstance(dst, Address)
94 | assert isinstance(dai, Wad)
95 |
96 | return Transact(self, self.web3, self.abi, self.address, self._contract, 'exit',
97 | [dst.address, dai.value])
98 |
99 | def exitAll(self, dst: Address) -> Transact:
100 | """ Free all ERC20 Dai from the DSR Contract and give to dst address """
101 | assert isinstance(dst, Address)
102 |
103 | return Transact(self, self.web3, self.abi, self.address, self._contract, 'exitAll', [dst.address])
104 |
105 | def __repr__(self):
106 | return f"DsrManager('{self.address}')"
107 |
--------------------------------------------------------------------------------
/pymaker/abi/OSM.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"src_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"val","type":"bytes32"}],"name":"LogValue","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"bud","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"src_","type":"address"}],"name":"change","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address[]","name":"a","type":"address[]"}],"name":"diss","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"a","type":"address"}],"name":"diss","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"hop","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address[]","name":"a","type":"address[]"}],"name":"kiss","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"a","type":"address"}],"name":"kiss","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pass","outputs":[{"internalType":"bool","name":"ok","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"peek","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"peep","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"poke","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"read","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"src","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"start","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint16","name":"ts","type":"uint16"}],"name":"step","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"stop","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"stopped","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"void","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"zzz","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/tests/manual_test_mcd.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2019-2020 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import os
19 | import sys
20 | from web3 import Web3, HTTPProvider
21 |
22 | from pymaker import Address
23 | from pymaker.deployment import DssDeployment
24 | from pymaker.keys import register_keys
25 | from pymaker.numeric import Wad
26 | from pymaker.oracles import OSM
27 |
28 | assert os.environ['ETH_RPC_URL']
29 | web3 = Web3(HTTPProvider(endpoint_uri=os.environ['ETH_RPC_URL'], request_kwargs={"timeout": 10}))
30 | our_address = None
31 | if len(sys.argv) > 1:
32 | web3.eth.defaultAccount = sys.argv[1] # ex: 0x0000000000000000000000000000000aBcdef123
33 | our_address = Address(web3.eth.defaultAccount)
34 | if len(sys.argv) > 2:
35 | register_keys(web3, [sys.argv[2]]) # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
36 | run_transactions = True
37 | else:
38 | run_transactions = False
39 | mcd = DssDeployment.from_node(web3)
40 |
41 | # Print a list of collaterals available for this deployment of MCD
42 | for collateral in mcd.collaterals.values():
43 | osm: OSM = collateral.pip
44 | liquidation = "clip" if collateral.clipper else "flip"
45 | print(f"Found {collateral.ilk.name:>15} - {collateral.gem.name():<21} with {collateral.adapter.dec():>2} decimals, "
46 | f"OSM price {round(float(osm.peek()), 3):>14}, "
47 | f"rate {float(collateral.ilk.rate):<18}, using {liquidation} liquidations")
48 |
49 | # Choose the desired collateral; in this case we'll wrap some Eth
50 | collateral = mcd.collaterals['ETH-A']
51 | ilk = collateral.ilk
52 |
53 | if run_transactions:
54 | # Determine minimum amount of Dai which can be drawn
55 | dai_amount = Wad(ilk.dust)
56 | # Set an amount of collateral to join and an amount of Dai to draw
57 | collateral_amount = Wad.from_number(105)
58 | if collateral.gem.balance_of(our_address) > collateral_amount:
59 | if collateral.ilk.name.startswith("ETH"):
60 | # Wrap ETH to produce WETH
61 | assert collateral.gem.deposit(collateral_amount).transact()
62 |
63 | # Add collateral and allocate the desired amount of Dai
64 | collateral.approve(our_address)
65 | assert collateral.adapter.join(our_address, collateral_amount).transact()
66 | assert mcd.vat.frob(ilk, our_address, dink=collateral_amount, dart=Wad(0)).transact()
67 | assert mcd.vat.frob(ilk, our_address, dink=Wad(0), dart=dai_amount).transact()
68 | print(f"Urn balance: {mcd.vat.urn(ilk, our_address)}")
69 | print(f"Dai balance: {mcd.vat.dai(our_address)}")
70 |
71 | # Mint and withdraw our Dai
72 | mcd.approve_dai(our_address)
73 | assert mcd.dai_adapter.exit(our_address, dai_amount).transact()
74 | print(f"Dai balance after withdrawal: {mcd.vat.dai(our_address)}")
75 |
76 | # Repay (and burn) our Dai
77 | assert mcd.dai_adapter.join(our_address, dai_amount).transact()
78 | print(f"Dai balance after repayment: {mcd.vat.dai(our_address)}")
79 |
80 | # Withdraw our collateral; stability fee accumulation may make these revert
81 | assert mcd.vat.frob(ilk, our_address, dink=Wad(0), dart=dai_amount*-1).transact()
82 | assert mcd.vat.frob(ilk, our_address, dink=collateral_amount*-1, dart=Wad(0)).transact()
83 | assert collateral.adapter.exit(our_address, collateral_amount).transact()
84 | print(f"Dai balance w/o collateral: {mcd.vat.dai(our_address)}")
85 | else:
86 | print(f"Not enough {ilk.name} to join to the vat")
87 |
88 | if our_address:
89 | print(f"Collateral balance: {mcd.vat.gem(ilk, our_address)}")
90 | print(f"Urn balance: {mcd.vat.urn(ilk, our_address)}")
91 |
--------------------------------------------------------------------------------
/pymaker/abi/Cat.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"ilk","type":"bytes32"},{"indexed":true,"internalType":"address","name":"urn","type":"address"},{"indexed":false,"internalType":"uint256","name":"ink","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"art","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tab","type":"uint256"},{"indexed":false,"internalType":"address","name":"flip","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Bite","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"},{"internalType":"address","name":"urn","type":"address"}],"name":"bite","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"box","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"rad","type":"uint256"}],"name":"claw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"},{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"address","name":"data","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"ilk","type":"bytes32"},{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"address","name":"flip","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"ilks","outputs":[{"internalType":"address","name":"flip","type":"address"},{"internalType":"uint256","name":"chop","type":"uint256"},{"internalType":"uint256","name":"dunk","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"litter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"live","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"vow","outputs":[{"internalType":"contract VowLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]
2 |
--------------------------------------------------------------------------------
/tests/manual_test_async_tx.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2020 EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import asyncio
19 | import logging
20 | import os
21 | import sys
22 | import threading
23 | import time
24 |
25 | from pymaker import Address, web3_via_http
26 | from pymaker.deployment import DssDeployment
27 | from pymaker.gas import FixedGasPrice, GeometricGasPrice
28 | from pymaker.keys import register_keys
29 | from pymaker.numeric import Wad
30 |
31 | logging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)
32 | # reduce logspew
33 | logging.getLogger('urllib3').setLevel(logging.INFO)
34 | logging.getLogger("web3").setLevel(logging.INFO)
35 | logging.getLogger("asyncio").setLevel(logging.INFO)
36 | logging.getLogger("requests").setLevel(logging.INFO)
37 |
38 | pool_size = int(sys.argv[3]) if len(sys.argv) > 3 else 10
39 | web3 = web3_via_http(endpoint_uri=os.environ['ETH_RPC_URL'], http_pool_size=pool_size)
40 | web3.eth.defaultAccount = sys.argv[1] # ex: 0x0000000000000000000000000000000aBcdef123
41 | register_keys(web3, [sys.argv[2]]) # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
42 |
43 | mcd = DssDeployment.from_node(web3)
44 | our_address = Address(web3.eth.defaultAccount)
45 | weth = DssDeployment.from_node(web3).collaterals['ETH-A'].gem
46 |
47 | GWEI = 1000000000
48 | slow_gas = GeometricGasPrice(initial_price=int(15 * GWEI), every_secs=42, max_price=200 * GWEI)
49 | fast_gas = GeometricGasPrice(initial_price=int(30 * GWEI), every_secs=42, max_price=200 * GWEI)
50 |
51 |
52 | class TestApp:
53 | def main(self):
54 | self.test_replacement()
55 | self.test_simultaneous()
56 | self.shutdown()
57 |
58 | def test_replacement(self):
59 | first_tx = weth.deposit(Wad(4))
60 | logging.info(f"Submitting first TX with gas price deliberately too low")
61 | self._run_future(first_tx.transact_async(gas_price=slow_gas))
62 | time.sleep(0.5)
63 |
64 | second_tx = weth.deposit(Wad(6))
65 | logging.info(f"Replacing first TX with legitimate gas price")
66 | second_tx.transact(replace=first_tx, gas_price=fast_gas)
67 |
68 | assert first_tx.replaced
69 |
70 | def test_simultaneous(self):
71 | self._run_future(weth.deposit(Wad(1)).transact_async(gas_price=fast_gas))
72 | self._run_future(weth.deposit(Wad(3)).transact_async(gas_price=fast_gas))
73 | self._run_future(weth.deposit(Wad(5)).transact_async(gas_price=fast_gas))
74 | self._run_future(weth.deposit(Wad(7)).transact_async(gas_price=fast_gas))
75 | time.sleep(33)
76 |
77 | def shutdown(self):
78 | balance = weth.balance_of(our_address)
79 | if Wad(0) < balance < Wad(100): # this account's tiny WETH balance came from this test
80 | logging.info(f"Unwrapping {balance} WETH")
81 | assert weth.withdraw(balance).transact(gas_price=fast_gas)
82 | elif balance >= Wad(22): # user already had a balance, so unwrap what a successful test would have consumed
83 | logging.info(f"Unwrapping 12 WETH")
84 | assert weth.withdraw(Wad(22)).transact(gas_price=fast_gas)
85 |
86 | @staticmethod
87 | def _run_future(future):
88 | def worker():
89 | loop = asyncio.new_event_loop()
90 | asyncio.set_event_loop(loop)
91 | try:
92 | asyncio.get_event_loop().run_until_complete(future)
93 | finally:
94 | loop.close()
95 |
96 | thread = threading.Thread(target=worker, daemon=True)
97 | thread.start()
98 |
99 |
100 | if __name__ == '__main__':
101 | TestApp().main()
102 |
--------------------------------------------------------------------------------
/pymaker/abi/SaiTap.abi:
--------------------------------------------------------------------------------
1 | [{"constant":false,"inputs":[],"name":"heal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"sin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"skr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"vent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"cash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"woe","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tub","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"mock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"bid","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"joy","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"s2s","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"off","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"vox","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gap","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fog","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"sai","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"param","type":"bytes32"},{"name":"val","type":"uint256"}],"name":"mold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"fix_","type":"uint256"}],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"fix","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"bust","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"boom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"ask","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"tub_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]
--------------------------------------------------------------------------------
/pymaker/auth.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2018 reverendus
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | from web3 import Web3
19 |
20 | from pymaker import Contract, Address, Transact
21 | from pymaker.util import int_to_bytes32
22 |
23 |
24 | class DSGuard(Contract):
25 | """A client for the `DSGuard` contract.
26 |
27 | You can find the source code of the `DSGuard` contract here:
28 | .
29 |
30 | Attributes:
31 | web3: An instance of `Web` from `web3.py`.
32 | address: Ethereum address of the `DSGuard` contract.
33 | """
34 |
35 | abi = Contract._load_abi(__name__, 'abi/DSGuard.abi')
36 | bin = Contract._load_bin(__name__, 'abi/DSGuard.bin')
37 |
38 | ANY = int_to_bytes32(2 ** 256 - 1)
39 |
40 | def __init__(self, web3: Web3, address: Address):
41 | assert(isinstance(web3, Web3))
42 | assert(isinstance(address, Address))
43 |
44 | self.web3 = web3
45 | self.address = address
46 | self._contract = self._get_contract(web3, self.abi, address)
47 |
48 | @staticmethod
49 | def deploy(web3: Web3):
50 | return DSGuard(web3=web3, address=Contract._deploy(web3, DSGuard.abi, DSGuard.bin, []))
51 |
52 | def permit(self, src, dst, sig: bytes) -> Transact:
53 | """Grant access to a function call.
54 |
55 | Args:
56 | src: Address of the caller, or `ANY`.
57 | dst: Address of the called contract, or `ANY`.
58 | sig: Signature of the called function, or `ANY`.
59 |
60 | Returns:
61 | A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.
62 | """
63 | assert(isinstance(src, Address) or isinstance(src, bytes))
64 | assert(isinstance(dst, Address) or isinstance(dst, bytes))
65 | assert(isinstance(sig, bytes) and len(sig) in (4, 32))
66 |
67 | if isinstance(src, Address) and isinstance(dst, Address):
68 | method = 'permit(address,address,bytes32)'
69 | src = src.address
70 | dst = dst.address
71 |
72 | else:
73 | method = 'permit(bytes32,bytes32,bytes32)'
74 |
75 | return Transact(self, self.web3, self.abi, self.address, self._contract, method, [src, dst, sig])
76 |
77 | def __repr__(self):
78 | return f"DSGuard('{self.address}')"
79 |
80 |
81 | # TODO: Complete implementation and unit test
82 | class DSAuth(Contract):
83 |
84 | abi = Contract._load_abi(__name__, 'abi/DSAuth.abi')
85 | bin = Contract._load_bin(__name__, 'abi/DSAuth.bin')
86 |
87 | def __init__(self, web3: Web3, address: Address):
88 | assert (isinstance(web3, Web3))
89 | assert (isinstance(address, Address))
90 |
91 | self.web3 = web3
92 | self.address = address
93 | self._contract = self._get_contract(web3, self.abi, address)
94 |
95 | @staticmethod
96 | def deploy(web3: Web3):
97 | return DSAuth(web3=web3, address=Contract._deploy(web3, DSAuth.abi, DSAuth.bin, []))
98 |
99 | def get_owner(self) -> Address:
100 | return Address(self._contract.functions.owner().call())
101 |
102 | def set_owner(self, owner: Address) -> Transact:
103 | assert isinstance(owner, Address)
104 |
105 | return Transact(self, self.web3, self.abi, self.address, self._contract,
106 | "setOwner", [owner.address])
107 |
108 | def set_authority(self, ds_authority: Address):
109 | assert isinstance(ds_authority, Address)
110 |
111 | return Transact(self, self.web3, self.abi, self.address, self._contract,
112 | "setAuthority", [ds_authority.address])
113 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | # This file is part of Maker Keeper Framework.
2 | #
3 | # Copyright (C) 2017-2019 reverendus, EdNoepel
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Affero General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU Affero General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Affero General Public License
16 | # along with this program. If not, see .
17 |
18 | import logging
19 | import pytest
20 |
21 | from web3 import Web3, HTTPProvider
22 |
23 | from pymaker import Address, web3_via_http
24 | from pymaker.auctions import Flipper, Flapper, Flopper
25 | from pymaker.deployment import Deployment, DssDeployment
26 | from pymaker.dss import Vat, Vow, Cat, Dog, Jug, Pot
27 | from pymaker.keys import register_keys
28 |
29 |
30 | @pytest.fixture(scope='session')
31 | def new_deployment() -> Deployment:
32 | return Deployment()
33 |
34 |
35 | @pytest.fixture()
36 | def deployment(new_deployment: Deployment) -> Deployment:
37 | new_deployment.reset()
38 | return new_deployment
39 |
40 |
41 | @pytest.fixture(scope="session")
42 | def web3() -> Web3:
43 | # for local dockerized parity testchain
44 | web3 = web3_via_http("http://0.0.0.0:8545")
45 | web3.eth.defaultAccount = "0x50FF810797f75f6bfbf2227442e0c961a8562F4C"
46 | register_keys(web3,
47 | ["key_file=tests/config/keys/UnlimitedChain/key1.json,pass_file=/dev/null",
48 | "key_file=tests/config/keys/UnlimitedChain/key2.json,pass_file=/dev/null",
49 | "key_file=tests/config/keys/UnlimitedChain/key3.json,pass_file=/dev/null",
50 | "key_file=tests/config/keys/UnlimitedChain/key4.json,pass_file=/dev/null",
51 | "key_file=tests/config/keys/UnlimitedChain/key.json,pass_file=/dev/null"])
52 |
53 | # reduce logspew
54 | logging.getLogger("web3").setLevel(logging.INFO)
55 | logging.getLogger("urllib3").setLevel(logging.INFO)
56 | logging.getLogger("asyncio").setLevel(logging.INFO)
57 |
58 | assert len(web3.eth.accounts) > 3
59 | return web3
60 |
61 |
62 | @pytest.fixture(scope="session")
63 | def our_address(web3) -> Address:
64 | return Address(web3.eth.accounts[0])
65 |
66 |
67 | @pytest.fixture(scope="session")
68 | def other_address(web3) -> Address:
69 | return Address(web3.eth.accounts[1])
70 |
71 |
72 | @pytest.fixture(scope="session")
73 | def deployment_address(web3) -> Address:
74 | # FIXME: Unsure why it isn't added to web3.eth.accounts list
75 | return Address("0x00a329c0648769A73afAc7F9381E08FB43dBEA72")
76 |
77 |
78 | @pytest.fixture(scope="session")
79 | def mcd(web3) -> DssDeployment:
80 | # for local dockerized parity testchain
81 | deployment = DssDeployment.from_node(web3=web3)
82 | validate_contracts_loaded(deployment)
83 | initialize_collaterals(deployment)
84 | return deployment
85 |
86 |
87 | def validate_contracts_loaded(deployment: DssDeployment):
88 | assert isinstance(deployment.vat, Vat)
89 | assert deployment.vat.address is not None
90 | assert isinstance(deployment.vow, Vow)
91 | assert deployment.vow.address is not None
92 | assert isinstance(deployment.cat, Cat)
93 | assert deployment.cat.address is not None
94 | assert isinstance(deployment.dog, Dog)
95 | assert deployment.dog.address is not None
96 | assert isinstance(deployment.jug, Jug)
97 | assert deployment.jug.address is not None
98 | assert isinstance(deployment.flapper, Flapper)
99 | assert deployment.flapper.address is not None
100 | assert isinstance(deployment.flopper, Flopper)
101 | assert deployment.flopper.address is not None
102 | assert isinstance(deployment.pot, Pot)
103 | assert deployment.pot.address is not None
104 |
105 |
106 | def initialize_collaterals(deployment: DssDeployment):
107 | for collateral in deployment.collaterals.values():
108 | if collateral.clipper:
109 | collateral.clipper.upchost().transact(from_address=deployment_address(deployment.web3))
110 |
--------------------------------------------------------------------------------
/pymaker/abi/Flapper.abi:
--------------------------------------------------------------------------------
1 | [{"inputs":[{"internalType":"address","name":"vat_","type":"address"},{"internalType":"address","name":"gem_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bid","type":"uint256"}],"name":"Kick","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"constant":true,"inputs":[],"name":"beg","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"bids","outputs":[{"internalType":"uint256","name":"bid","type":"uint256"},{"internalType":"uint256","name":"lot","type":"uint256"},{"internalType":"address","name":"guy","type":"address"},{"internalType":"uint48","name":"tic","type":"uint48"},{"internalType":"uint48","name":"end","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"rad","type":"uint256"}],"name":"cage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"deal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"gem","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"lot","type":"uint256"},{"internalType":"uint256","name":"bid","type":"uint256"}],"name":"kick","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"kicks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"live","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tau","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"lot","type":"uint256"},{"internalType":"uint256","name":"bid","type":"uint256"}],"name":"tend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tick","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ttl","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"yank","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
2 |
--------------------------------------------------------------------------------
/config/testnet-addresses.json:
--------------------------------------------------------------------------------
1 | {
2 | "DEPLOYER": "0x00a329c0648769A73afAc7F9381E08FB43dBEA72",
3 | "MULTICALL": "0x492934308E98b590A626666B703A6dDf2120e85e",
4 | "FAUCET": "0x0A64DF94bc0E039474DB42bb52FEca0c1d540402",
5 | "MCD_DEPLOY": "0xd29915F1A3fF9846fE5D8d9d2C954de21932AF7F",
6 | "MCD_GOV": "0x1FD8397e8108ada12eC07976D92F773364ba46e7",
7 | "GOV_GUARD": "0x39a812a6aA4C475b6562B73Bf0584eb3655e8D6C",
8 | "MCD_IOU": "0xDfBc5fbEaa41bD1cd15F9d6b77265DBc3CB2A677",
9 | "MCD_ADM": "0x8c39d4833812A7516BaCD455dA7F97f0a8C11B05",
10 | "VOTE_PROXY_FACTORY": "0x2dC383E93ec3DB735777a3E9ae69E2aD81edaF03",
11 | "MCD_VAT": "0x3D72e5B28FbA05Bd4090A2A587Bb3eCC899f33b2",
12 | "MCD_JUG": "0x77a371Ed06fbA2D93D05C5bDE6d8eC58b3a35fbd",
13 | "MCD_CAT": "0x31865076D1E28ad4eA06D5Db7aAa4AAF225f1Fb5",
14 | "MCD_DOG": "0xd9b3B2429F1b301156Bf3103419ef6B78E888386",
15 | "MCD_VOW": "0xA2F9C8C13118c88f14501cDCB2b52Af3751622ae",
16 | "MCD_JOIN_DAI": "0x7f8241b7250c5C5368788543E4dA2F9A919E9F02",
17 | "MCD_FLAP": "0xB5054202380d093A02916e0137d75b54D6182A23",
18 | "MCD_FLOP": "0xd57D9931b305f1bc1622B97c8Cc6747E4A9254a0",
19 | "MCD_PAUSE": "0x967aE1FB90aA36C7d3B16B5328504F542495D952",
20 | "MCD_PAUSE_PROXY": "0x3689de8F568e4A59254eE7eaB1A37d87044f52Da",
21 | "MCD_GOV_ACTIONS": "0x2287909BB95FA078C73CC2d5a5AF6fE1244b0911",
22 | "MCD_DAI": "0x8A1567046e610Fec30F120BB70Df94B50561C1d3",
23 | "MCD_SPOT": "0x5422Ee4e22603E336905CDC9D59aE3F0012fe4c8",
24 | "MCD_POT": "0xe51e9A4D22b7451c0232508455195E7c0a6e0f19",
25 | "MCD_END": "0x27547dE5f11122283825Adf97FB98eF301f5F73c",
26 | "MCD_ESM": "0x8a606fD18B9f0CA3Cf480e70639c58EAa98d7389",
27 | "PROXY_ACTIONS": "0x84617303947304444Ceb641582c024f277BBF4Ff",
28 | "PROXY_ACTIONS_END": "0x78c362A5690447EA2BBC3E8008502efD13936F79",
29 | "PROXY_ACTIONS_DSR": "0x277aD07109FE52a742B808a3E6765Ee1Ad0e7Ad2",
30 | "CDP_MANAGER": "0x79a8FC3D98Fc84c9BC2B3a737EA992321a1b86A3",
31 | "DSR_MANAGER": "0x0Faf2F31Ab165B55F42E55c8065c0EC7170A0d45",
32 | "GET_CDPS": "0x4f05AfbC371854D027263e756487BDefD099178f",
33 | "ILK_REGISTRY": "0x8e23974b151827f0E8151aC526C4c4c974c06A90",
34 | "OSM_MOM": "0x96724aa934979936aE5c3Afda1599b5ed61252ce",
35 | "FLIPPER_MOM": "0xcbfD09D76140D01E573b452bB984d82589571fC2",
36 | "CLIPPER_MOM": "0x9BB69befBAA567a7EaEE33b671756596517338F4",
37 | "MCD_IAM_AUTO_LINE": "0x01E354A7eF79962DbB690705e46bd54c1C855E80",
38 | "PROXY_FACTORY": "0x3DD0864668C36D27B53a98137764c99F9FD5B7B2",
39 | "PROXY_REGISTRY": "0x26C8d09E5C0B423E2827844c770F61c9af2870E7",
40 | "ETH": "0xEddA486ddB7eaa8f9FEce8c682EFD40f535b3Ad5",
41 | "VAL_ETH": "0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84",
42 | "MCD_JOIN_ETH_A": "0x9119B5d8b735E4cEbaE7386AF6cD2B863c7d35A8",
43 | "MCD_FLIP_ETH_A": "0x0BD7632aF5F7020575e59E80ABbca739035Ac0EC",
44 | "MCD_JOIN_ETH_B": "0xe2dD18a6000030F30ecB1237B15605533f814c59",
45 | "MCD_CLIP_ETH_B": "0x3f2603979a4A185acE9B9c941193704FfBD24F4A",
46 | "MCD_CLIP_CALC_ETH_B": "0x8cabea65F0140962A7D7Fe9f31a265a2B19Dc305",
47 | "MCD_JOIN_ETH_C": "0x9FdC3bBD89ae1fB19054241644EF3dfbdcA85544",
48 | "MCD_FLIP_ETH_C": "0xB0b7Db244994E9B922998f49b7ae61956314CA35",
49 | "BAT": "0x39b4C0A63c4c16DD1816D104F2C18a296Dbd4e70",
50 | "VAL_BAT": "0x62d69f6867A0A084C6d313943dC22023Bc263691",
51 | "MCD_JOIN_BAT_A": "0xA616aD7D4562dCD9208425Af4038defD0a9057B0",
52 | "MCD_FLIP_BAT_A": "0xB36901dB56E2fb44862a7D0eAE9F5Cf9a7E449bD",
53 | "USDC": "0x32Ee2bF1267253f76298D4199095B9C6b5A389c0",
54 | "VAL_USDC": "0xee35211C4D9126D520bBfeaf3cFee5FE7B86F221",
55 | "MCD_JOIN_USDC_A": "0x59ea98A4b40B72140b7dc93c29c098AE607Ce20D",
56 | "MCD_FLIP_USDC_A": "0xE8a124764cCcb7Ee3E8e320aAaA841Ea249197D4",
57 | "MCD_JOIN_USDC_B": "0xaD1Bf7D34Fa48f7Cf7CA1CE3c7408f9151DF2745",
58 | "MCD_FLIP_USDC_B": "0xC8055bC4415Ac354fA6EFbC3bcf57d5cBcc072ed",
59 | "TUSD": "0xB014e899ddb9a55af72fE09E8570E700A5167b6d",
60 | "VAL_TUSD": "0x7C276DcAab99BD16163c1bcce671CaD6A1ec0945",
61 | "MCD_JOIN_TUSD_A": "0xAFB95880bc835B6Eeb041ce57D570D013360beC6",
62 | "MCD_FLIP_TUSD_A": "0x6b0f809E52218192AbAf32C9C74F31229E07B626",
63 | "WBTC": "0x123010c0Fe7D4d7420f309431bb95060393fe3B7",
64 | "VAL_WBTC": "0x3f85D0b6119B38b7E6B119F7550290fec4BE0e3c",
65 | "MCD_JOIN_WBTC_A": "0xf4C33a989bD0C9e9268c5bfCbCE2C8501B9dBa25",
66 | "MCD_FLIP_WBTC_A": "0x53781B6DC81F5b90421371172c1b06e384858e44",
67 | "GUSD": "0x0363Ef677c78bd8C8302DB33be2F6629E33E72Fe",
68 | "VAL_GUSD": "0xd5F051401ca478B34C80D0B5A119e437Dc6D9df5",
69 | "MCD_JOIN_GUSD_A": "0x01C957395029E9aCCbCb25a6Ab72C618252CACf9",
70 | "MCD_FLIP_GUSD_A": "0x334490acE32D96808B104A1f8723cFAB5881DE46",
71 | "PROXY_PAUSE_ACTIONS": "0x23263d4ebB1190A483A84e90B9a6Dd8720979284",
72 | "PROXY_DEPLOYER": "0x30c8860f6a38819B59E1255A499A10bCBF4Ee747"
73 | }
--------------------------------------------------------------------------------