├── .gitattributes ├── .gitignore ├── README.md ├── REGISTRY.md ├── SUMMARY.md ├── audits └── PeckShield-Audit-Report-Keep3r-v1.0.pdf ├── contracts ├── Keep3r.sol ├── Keep3rGovernance.sol ├── Keep3rJob.sol ├── Keep3rV1Helper.sol ├── Keep3rV1JobRegistry.sol ├── Keep3rV1Volatility.sol ├── Keep3rV2Helper.sol ├── Keep3rV2OracleFactory.sol ├── MetaKeep3r.sol ├── Migrations.sol ├── jobs │ ├── AaveLiquidateKeep3r.sol │ ├── BzxLiquidate.sol │ ├── CompoundFlashLiquidations.sol │ ├── CreamFlashLiquidations.sol │ ├── CreamLiquidate.sol │ ├── HegicPoolKeep3r.sol │ ├── Keep3rV1Oracle.sol │ ├── SushiswapV2Keep3r.sol │ ├── SynthetixKeep3r.sol │ ├── UniswapV2Oracle.sol │ └── YearnV1EarnKeep3r.sol └── utils │ ├── cdf.sol │ └── logn.sol ├── docs ├── ERC20.md ├── Factory.md ├── Governance.md ├── JOBS.md ├── Keep3r.md ├── Keep3rHelper.md ├── Keepers.md ├── SafeMath.md ├── Uniswap.md ├── UniswapPair.md └── WETH9.md ├── migrations └── 1_initial_migration.js ├── scripts ├── HegicPoolKeeper.js ├── Keep3rV1Oracle.js ├── MMStrategyKeeperV2.js ├── UniswapV2OracleKeeper.js └── YearnV1EarnKeeper.js ├── test ├── .gitkeep ├── CompoundFlashLiquidationTest.sol ├── CreamFlashLiquidationTest.sol ├── Keep3rTest.sol ├── MetaKeep3rTest.sol ├── REPL.sol ├── SushiswapV2Keep3rTest.sol └── script.sol └── truffle-config.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Brownie 2 | __pycache__ 3 | .history 4 | .hypothesis/ 5 | build/ 6 | reports/ 7 | 8 | # IDEs 9 | /.idea 10 | 11 | # misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Keep3r Network 2 | 3 | _These docs are in active development by the Keep3r community._ 4 | 5 | Keep3r Network is a decentralized keeper network for projects that need external devops and for external teams to find keeper jobs 6 | 7 | ## Keepers 8 | 9 | A Keeper is the term used to refer to an external person and/or team that executes a job. This can be as simplistic as calling a transaction, or as complex as requiring extensive off-chain logic. The scope of Keep3r network is not to manage these jobs themselves, but to allow contracts to register as jobs for keepers, and keepers to register themselves as available to perform jobs. It is up to the individual keeper to set up their devops and infrastructure and create their own rules based on what transactions they deem profitable. 10 | 11 | ## Jobs 12 | 13 | A Job is the term used to refer to a smart contract that wishes an external entity to perform an action. They would like the action to be performed in "good will" and not have a malicious result. For this reason they register as a job, and keepers can then execute on their contract. 14 | 15 | ### Becoming a Keeper 16 | 17 | To join as a Keeper you call ```bond(uint)``` on the Keep3r contract. You do not need to have KPR tokens to join as a Keeper, so you can join with ```bond(0)```. There is a 3 day bonding delay before you can activate as a Keeper. Once the 3 days have passed, you can call ```activate()```. Once activated you ```lastJob``` timestamp will be set to the current block timestamp. 18 | 19 | ### Registering a Job 20 | 21 | A job can be any system that requires external execution, the scope of Keep3r is not to define or restrict the action taken, but to create an incentive mechanism for all parties involved. There are two cores ways to create a Job; 22 | 23 | #### Registering a Job via Governance 24 | 25 | If you prefer, you can register as a job by simply submitting a proposal via Governance, to include the contract as a job. If governance approves, no further steps are required. 26 | 27 | #### Registering a Job via Contract Interface 28 | 29 | You can register as a job by calling ```addLiquidityToJob(address,uint)``` on the Keep3r contract. You must not have any current active jobs associated with this account. Calling ```addLiquidityToJob(address,uint)``` will create a pending Governance vote for the job specified by address in the function arguments. You are limited to submit a new job request via this address every ```14 days```. 30 | 31 | ## Job Interface 32 | 33 | Some contracts require external event execution, an example for this is the ```harvest()``` function in the yearn ecosystem, or the ```update(address,address)``` function in the uniquote ecosystem. These normally require a restricted access control list, however these can be difficult for fully decentralized projects to manage, as they lack devops infrastructure. 34 | 35 | These interfaces can be broken down into two types, no risk delta (something like ```update(address,address)``` in uniquote, which needs to be executed, but not risk to execution), and ```harvest()``` in yearn, which can be exploited by malicious actors by front-running deposits. 36 | 37 | For no, or low risk executions, you can simply call ```Keep3r.isKeeper(msg.sender)``` which will let you know if the given actor is a keeper in the network. 38 | 39 | For high, sensitive, or critical risk executions, you can specify a minimum bond, minimum jobs completed, and minimum Keeper age required to execute this function. Based on these 3 limits you can define your own trust ratio on these keepers. 40 | 41 | So a function definition would look as follows; 42 | ```solidity 43 | function execute() external { 44 | require(Keep3r.isKeeper(msg.sender), "Keep3r not allowed"); 45 | } 46 | ``` 47 | 48 | At the end of the call, you simply need to call ```workReceipt(address,uint)``` to finalize the execution for the keeper network. In the call you specify the keeper being rewarded, and the amount of KPR you would like to award them with. This is variable based on what you deem is a fair reward for the work executed. 49 | 50 | Example Keep3rJob 51 | 52 | ```solidity 53 | interface UniOracleFactory { 54 | function update(address tokenA, address tokenB) external; 55 | } 56 | 57 | interface Keep3r { 58 | function isKeeper(address) external view returns (bool); 59 | function workReceipt(address keeper, uint amount) external; 60 | } 61 | 62 | contract Keep3rJob { 63 | UniOracleFactory constant JOB = UniOracleFactory(0x61da8b0808CEA5281A912Cd85421A6D12261D136); 64 | Keep3r constant KPR = Keep3r(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 65 | 66 | function update(address tokenA, address tokenB) external { 67 | require(KPR.isKeeper(msg.sender), "Keep3rJob::update: not a valid keeper"); 68 | JOB.update(tokenA, tokenB); 69 | KPR.workReceipt(msg.sender, 1e18); 70 | } 71 | } 72 | ``` 73 | 74 | ### Job Credits 75 | 76 | As mentioned in Job Interface, a job has a set amount of ```credits``` that they can award keepers with. To receive ```credits``` you do not need to purchase KPR tokens, instead you need to provide KPR-WETH liquidity in Uniswap. This will give you an amount of credits equal to the amount of KPR tokens in the liquidity you provide. 77 | 78 | You can remove your liquidity at any time, so you do not have to keep buying new credits. Your liquidity provided is never reduced and as such you can remove it whenever you no longer would like a job to be executed. 79 | 80 | To add credits, you simply need to have KPR-WETH LP tokens, you then call ```addLiquidityToJob(address,uint)``` specifying the job in the address and the amount in the uint. This will then transfer your LP tokens to the contract and keep them in escrow. You can remove your liquidity at any time by calling ```unbondLiquidityFromJob()```, this will allow you to remove the liquidity after 14 days by calling ```removeLiquidityFromJob()``` 81 | 82 | ## Github 83 | 84 | [Keep3r](https://github.com/keep3r-network/keep3r.network) 85 | -------------------------------------------------------------------------------- /REGISTRY.md: -------------------------------------------------------------------------------- 1 | ## Beta Addresses 2 | Description | Address 3 | --- | --- 4 | Keep3rV1 | [0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44](https://etherscan.io/address/0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44) (rc.1.2-a) 5 | Keep3rV1Library | [0xfc38B6eBA9d47CBFc8C7B4FFfFd142B78996B6f1](https://etherscan.io/address/0xfc38B6eBA9d47CBFc8C7B4FFfFd142B78996B6f1) 6 | Keep3rV1Helper | [0x2720535578096f1dE6C8c9B5255F1Bda40e8067A](https://etherscan.io/address/0x2720535578096f1dE6C8c9B5255F1Bda40e8067A) 7 | Keep3rV1JobRegistry | [0x7396899638410094B3690F8bd2b56f07fdAb620c](https://etherscan.io/address/0x7396899638410094b3690f8bd2b56f07fdab620c) 8 | Keep3rV1Governance | [0xc7212Fc959bBB606F97036e8Ac3DA7AaBf0cb735](https://etherscan.io/address/0xc7212fc959bbb606f97036e8ac3da7aabf0cb735) 9 | Keep3rV1Treasury | [0xf7aa325404f81cf34268657ddf2d046763a8c4ed](https://etherscan.io/address/0xf7aa325404f81cf34268657ddf2d046763a8c4ed) 10 | MetaKeep3r | [0x93Dfa873b15ad496BA8116Ce6CfEC52eF30a9372](https://etherscan.io/address/0x93dfa873b15ad496ba8116ce6cfec52ef30a9372) 11 | 12 | ## Jobs 13 | 14 | Job | Address 15 | --- | --- 16 | HegicPoolKeep3r | [0x5DDe926b0A31346f2485900C5e64c2577F43F774](https://etherscan.io/address/0x5DDe926b0A31346f2485900C5e64c2577F43F774) 17 | YearnV1EarnKeep3r | [0xe7F4ab593aeC81EcA754Da1B3B7cE0C42a13Ec0C](https://etherscan.io/address/0xe7F4ab593aeC81EcA754Da1B3B7cE0C42a13Ec0C) 18 | MetaKeep3r | [0x93Dfa873b15ad496BA8116Ce6CfEC52eF30a9372](https://etherscan.io/address/0x93dfa873b15ad496ba8116ce6cfec52ef30a9372) 19 | DforceStrategyKeep3r | [0x30084324619D9645019C3f2cb3a94611601a3078](https://etherscan.io/address/0x30084324619D9645019C3f2cb3a94611601a3078) 20 | HegicKeep3rV2 | [0xB1aCE96072654e3A2564A90D64Be99Dd3Ac195F4](https://etherscan.io/address/0xB1aCE96072654e3A2564A90D64Be99Dd3Ac195F4) 21 | MMStrategyKeeperV1 | [0x4E504c6ca43cD1bBd9096A2c2E77A176D10910B1](https://etherscan.io/address/0x4E504c6ca43cD1bBd9096A2c2E77A176D10910B1) 22 | LidoKeep3r | [0x1EE5C83C4B43aaEd21613D5cc7835D36078ce03F](https://etherscan.io/address/0x1EE5C83C4B43aaEd21613D5cc7835D36078ce03F) 23 | HegicBotKeep3r | [0x6b405609B78241112a3030E8a85570F06fbd3aca](https://etherscan.io/address/0x6b405609B78241112a3030E8a85570F06fbd3aca) 24 | BzxLiquidateProxy | [0xB59A6dCE95bc446aD098B4C4b415bbe766068cb8](https://etherscan.io/address/0xB59A6dCE95bc446aD098B4C4b415bbe766068cb8) 25 | YearnLiquidationKeep3r | [0xf35eE77197b8E222549A54D7A43fc4DC60eBbeeb](https://etherscan.io/address/0xf35eE77197b8E222549A54D7A43fc4DC60eBbeeb) 26 | YearnV1EarnKeep3rV2 | [0xF8106d779246612FF7a6A623EF7026a9ccFaf709](https://etherscan.io/address/0xF8106d779246612FF7a6A623EF7026a9ccFaf709) 27 | Generic Keep3r for Mushrooms Finance | [0x0bD1d668d8E83d14252F2e01D5873df77A6511f0](https://etherscan.io/address/0x0bD1d668d8E83d14252F2e01D5873df77A6511f0) 28 | YearnTendV2Keep3rJob | [0x7b28163e7a3db17eF2dba02BCf7250A8Dc505057](https://etherscan.io/address/0x7b28163e7a3db17eF2dba02BCf7250A8Dc505057) 29 | PartialKeep3rV1OracleJob | [0x5efD850044Ba76b8ffE49437CB301be3568bA696](https://etherscan.io/address/0x5efD850044Ba76b8ffE49437CB301be3568bA696) 30 | Keep3rLiquidityManagerJob | [0x7E0Cc5edF2DD01FC543D698b7E00ff54c6c39085](https://etherscan.io/address/0x7E0Cc5edF2DD01FC543D698b7E00ff54c6c39085) 31 | HarvestV2Keep3rJob | [0x620bd1E1D1d845c8904aC03F6cd6b87706B7596b](https://etherscan.io/address/0x620bd1E1D1d845c8904aC03F6cd6b87706B7596b) 32 | CrvStrategyKeep3rJob | [0x02027bDA2425204f152B8aa35Fb78687D65E1AF5 ](https://etherscan.io/address/0x02027bDA2425204f152B8aa35Fb78687D65E1AF5 ) 33 | Keep3rV2OracleFactoryV2 | [0xaed599AADfEE8e32Cedb59db2b1120d33A7bACFD ](https://etherscan.io/address/0xaed599AADfEE8e32Cedb59db2b1120d33A7bACFD ) 34 | 35 | ### Pending 36 | 37 | Job | Address 38 | --- | --- 39 | 40 | ### Deprecated 41 | 42 | Job | Address 43 | --- | --- 44 | UniswapV2SlidingOracle | [0xd20b88Ca8bF84Ca829f7A9Cf0eC64e2bFE91c204](https://etherscan.io/address/0xd20b88Ca8bF84Ca829f7A9Cf0eC64e2bFE91c204) 45 | UniswapV2Oracle | [0x127a2975c4E1c75f1ed4757a861bbd42523DB035](https://etherscan.io/address/0x127a2975c4E1c75f1ed4757a861bbd42523DB035) 46 | AaveLiquidate | [0x5D18A46371e313fdC3BB66E77b10405087536e75](https://etherscan.io/address/0x5d18a46371e313fdc3bb66e77b10405087536e75) 47 | Keep3rOracle | [0x2ec4901ebBCE581bBAE029BA6405fcA5ab3B3d23](https://etherscan.io/address/0x2ec4901ebBCE581bBAE029BA6405fcA5ab3B3d23) 48 | Keep3rV1Oracle | [0x73353801921417F465377c8d898c6f4C0270282C](https://etherscan.io/address/0x73353801921417F465377c8d898c6f4C0270282C) 49 | SushiswapV1Oracle | [0xf67Ab1c914deE06Ba0F264031885Ea7B276a7cDa](https://etherscan.io/address/0xf67Ab1c914deE06Ba0F264031885Ea7B276a7cDa) 50 | AaveLiquidations | [0x78C992446a0272056c7f9c47e36b051D772486Dd](https://etherscan.io/address/0x78C992446a0272056c7f9c47e36b051D772486Dd) 51 | CreamLiquidate | [0xD7De27e74f1Ca2Ce413E19a0B30Fcc95395BFcd9](https://etherscan.io/address/0xd7de27e74f1ca2ce413e19a0b30fcc95395bfcd9) 52 | CompoundFlashLiquidations | [0x89970Ba0ba9d51f1Ab6d595E6A85E41C9C3806eb](https://etherscan.io/address/0x89970Ba0ba9d51f1Ab6d595E6A85E41C9C3806eb) 53 | UniswapV2SlidingOracle | [0xCA2E2df6A7a7Cf5bd19D112E8568910a6C2D3885](https://etherscan.io/address/0xCA2E2df6A7a7Cf5bd19D112E8568910a6C2D3885) 54 | 55 | ## Pairs 56 | 57 | Pair | Address 58 | --- | --- 59 | WETH | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 60 | WBTC | 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 61 | USDC | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 62 | USDT | 0xdAC17F958D2ee523a2206206994597C13D831ec7 63 | DAI | 0x6B175474E89094C44Da98b954EedeAC495271d0F 64 | UNI | 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 65 | LINK | 0x514910771AF9Ca656af840dff83E8264EcF986CA 66 | YFI | 0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e 67 | MKR | 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2 68 | AAVE | 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9 69 | SNX | 0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F 70 | COMP | 0xc00e94Cb662C3520282E6f5717214004A7f26888 71 | CRV | 0xD533a949740bb3306d119CC777fa900bA034cd52 72 | KPR | 0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44 73 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Introduction](README.md) 4 | 5 | * [Jobs](docs/JOBS.md) 6 | * [Keepers](docs/Keepers.md) 7 | * [Governance](docs/Governance.md) 8 | * [Registry](REGISTRY.md) 9 | 10 | * [Technical](docs/Keep3r.md) 11 | 12 | * [GitHub](https://github.com/keep3r-network/keep3r.network) 13 | -------------------------------------------------------------------------------- /audits/PeckShield-Audit-Report-Keep3r-v1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keep3r-network/keep3r.network/a6897007db6e656e0e310ed7ee4ad42904fe2794/audits/PeckShield-Audit-Report-Keep3r-v1.0.pdf -------------------------------------------------------------------------------- /contracts/Keep3rJob.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.6; 3 | 4 | interface UniOracleFactory { 5 | function update(address tokenA, address tokenB) external; 6 | } 7 | 8 | interface Keep3r { 9 | function isKeeper(address) external view returns (bool); 10 | function workReceipt(address keeper, uint amount) external; 11 | } 12 | 13 | interface Keep3rHelper { 14 | // Allows for variable pricing amounts 15 | function getQuoteFor(uint) external view returns (uint); 16 | } 17 | 18 | contract Keep3rJob { 19 | UniOracleFactory constant JOB = UniOracleFactory(0x61da8b0808CEA5281A912Cd85421A6D12261D136); 20 | Keep3r constant KPR = Keep3r(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 21 | Keep3rHelper constant KPRH = Keep3rHelper(0x0); 22 | // TODO: Add whitelist for approved contracts (worth paying for) 23 | // TODO: Get context values to know how much is a better value to pay out 24 | function update(address tokenA, address tokenB) external { 25 | require(KPR.isKeeper(msg.sender), "Keep3rJob::update: not a valid keeper"); 26 | JOB.update(tokenA, tokenB); 27 | KPR.workReceipt(msg.sender, KPRH.getQuoteFor(1e18)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/Keep3rV1Helper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.12; 3 | 4 | /** 5 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 6 | * checks. 7 | * 8 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 9 | * in bugs, because programmers usually assume that an overflow raises an 10 | * error, which is the standard behavior in high level programming languages. 11 | * `SafeMath` restores this intuition by reverting the transaction when an 12 | * operation overflows. 13 | * 14 | * Using this library instead of the unchecked operations eliminates an entire 15 | * class of bugs, so it's recommended to use it always. 16 | */ 17 | library SafeMath { 18 | /** 19 | * @dev Returns the addition of two unsigned integers, reverting on overflow. 20 | * 21 | * Counterpart to Solidity's `+` operator. 22 | * 23 | * Requirements: 24 | * - Addition cannot overflow. 25 | */ 26 | function add(uint a, uint b) internal pure returns (uint) { 27 | uint c = a + b; 28 | require(c >= a, "add: +"); 29 | 30 | return c; 31 | } 32 | 33 | /** 34 | * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow. 35 | * 36 | * Counterpart to Solidity's `+` operator. 37 | * 38 | * Requirements: 39 | * - Addition cannot overflow. 40 | */ 41 | function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 42 | uint c = a + b; 43 | require(c >= a, errorMessage); 44 | 45 | return c; 46 | } 47 | 48 | /** 49 | * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). 50 | * 51 | * Counterpart to Solidity's `-` operator. 52 | * 53 | * Requirements: 54 | * - Subtraction cannot underflow. 55 | */ 56 | function sub(uint a, uint b) internal pure returns (uint) { 57 | return sub(a, b, "sub: -"); 58 | } 59 | 60 | /** 61 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). 62 | * 63 | * Counterpart to Solidity's `-` operator. 64 | * 65 | * Requirements: 66 | * - Subtraction cannot underflow. 67 | */ 68 | function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 69 | require(b <= a, errorMessage); 70 | uint c = a - b; 71 | 72 | return c; 73 | } 74 | 75 | /** 76 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 77 | * 78 | * Counterpart to Solidity's `*` operator. 79 | * 80 | * Requirements: 81 | * - Multiplication cannot overflow. 82 | */ 83 | function mul(uint a, uint b) internal pure returns (uint) { 84 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 85 | // benefit is lost if 'b' is also tested. 86 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 87 | if (a == 0) { 88 | return 0; 89 | } 90 | 91 | uint c = a * b; 92 | require(c / a == b, "mul: *"); 93 | 94 | return c; 95 | } 96 | 97 | /** 98 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 99 | * 100 | * Counterpart to Solidity's `*` operator. 101 | * 102 | * Requirements: 103 | * - Multiplication cannot overflow. 104 | */ 105 | function mul(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 106 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 107 | // benefit is lost if 'b' is also tested. 108 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 109 | if (a == 0) { 110 | return 0; 111 | } 112 | 113 | uint c = a * b; 114 | require(c / a == b, errorMessage); 115 | 116 | return c; 117 | } 118 | 119 | /** 120 | * @dev Returns the integer division of two unsigned integers. 121 | * Reverts on division by zero. The result is rounded towards zero. 122 | * 123 | * Counterpart to Solidity's `/` operator. Note: this function uses a 124 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 125 | * uses an invalid opcode to revert (consuming all remaining gas). 126 | * 127 | * Requirements: 128 | * - The divisor cannot be zero. 129 | */ 130 | function div(uint a, uint b) internal pure returns (uint) { 131 | return div(a, b, "div: /"); 132 | } 133 | 134 | /** 135 | * @dev Returns the integer division of two unsigned integers. 136 | * Reverts with custom message on division by zero. The result is rounded towards zero. 137 | * 138 | * Counterpart to Solidity's `/` operator. Note: this function uses a 139 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 140 | * uses an invalid opcode to revert (consuming all remaining gas). 141 | * 142 | * Requirements: 143 | * - The divisor cannot be zero. 144 | */ 145 | function div(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 146 | // Solidity only automatically asserts when dividing by 0 147 | require(b > 0, errorMessage); 148 | uint c = a / b; 149 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 150 | 151 | return c; 152 | } 153 | 154 | /** 155 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 156 | * Reverts when dividing by zero. 157 | * 158 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 159 | * opcode (which leaves remaining gas untouched) while Solidity uses an 160 | * invalid opcode to revert (consuming all remaining gas). 161 | * 162 | * Requirements: 163 | * - The divisor cannot be zero. 164 | */ 165 | function mod(uint a, uint b) internal pure returns (uint) { 166 | return mod(a, b, "mod: %"); 167 | } 168 | 169 | /** 170 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 171 | * Reverts with custom message when dividing by zero. 172 | * 173 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 174 | * opcode (which leaves remaining gas untouched) while Solidity uses an 175 | * invalid opcode to revert (consuming all remaining gas). 176 | * 177 | * Requirements: 178 | * - The divisor cannot be zero. 179 | */ 180 | function mod(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 181 | require(b != 0, errorMessage); 182 | return a % b; 183 | } 184 | } 185 | 186 | library Math { 187 | /** 188 | * @dev Returns the largest of two numbers. 189 | */ 190 | function max(uint256 a, uint256 b) internal pure returns (uint256) { 191 | return a >= b ? a : b; 192 | } 193 | 194 | /** 195 | * @dev Returns the smallest of two numbers. 196 | */ 197 | function min(uint256 a, uint256 b) internal pure returns (uint256) { 198 | return a < b ? a : b; 199 | } 200 | 201 | /** 202 | * @dev Returns the average of two numbers. The result is rounded towards 203 | * zero. 204 | */ 205 | function average(uint256 a, uint256 b) internal pure returns (uint256) { 206 | // (a + b) / 2 can overflow, so we distribute 207 | return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); 208 | } 209 | } 210 | 211 | interface IChainLinkFeed { 212 | function latestAnswer() external view returns (int256); 213 | } 214 | 215 | interface IKeep3rV1 { 216 | function totalBonded() external view returns (uint); 217 | function bonds(address keeper, address credit) external view returns (uint); 218 | function votes(address keeper) external view returns (uint); 219 | } 220 | 221 | interface IUniswapV2SlidingOracle { 222 | function current(address tokenIn, uint amountIn, address tokenOut) external view returns (uint); 223 | } 224 | 225 | contract Keep3rV1Helper { 226 | using SafeMath for uint; 227 | 228 | IChainLinkFeed public constant FASTGAS = IChainLinkFeed(0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C); 229 | IKeep3rV1 public constant KP3R = IKeep3rV1(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 230 | IUniswapV2SlidingOracle public constant UV2SO = IUniswapV2SlidingOracle(0x73353801921417F465377c8d898c6f4C0270282C); 231 | address public constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 232 | 233 | uint constant public MAX = 11; 234 | uint constant public BASE = 10; 235 | uint constant public SWAP = 300000; 236 | uint constant public TARGETBOND = 200e18; 237 | 238 | function quote(uint eth) public view returns (uint) { 239 | return UV2SO.current(address(WETH), eth, address(KP3R)); 240 | } 241 | 242 | function getFastGas() external view returns (uint) { 243 | return uint(FASTGAS.latestAnswer()); 244 | } 245 | 246 | function bonds(address keeper) public view returns (uint) { 247 | return KP3R.bonds(keeper, address(KP3R)).add(KP3R.votes(keeper)); 248 | } 249 | 250 | function getQuoteLimitFor(address origin, uint gasUsed) public view returns (uint) { 251 | uint _min = quote((gasUsed.add(SWAP)).mul(uint(FASTGAS.latestAnswer()))); 252 | uint _boost = _min.mul(MAX).div(BASE); 253 | uint _bond = Math.min(bonds(origin), TARGETBOND); 254 | return Math.max(_min, _boost.mul(_bond).div(TARGETBOND)); 255 | } 256 | 257 | function getQuoteLimit(uint gasUsed) external view returns (uint) { 258 | return getQuoteLimitFor(tx.origin, gasUsed); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /contracts/Keep3rV1JobRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.12; 3 | 4 | contract Keep3rV1JobRegistry { 5 | /// @notice governance address for the governance contract 6 | address public governance; 7 | address public pendingGovernance; 8 | 9 | struct _job { 10 | uint _id; 11 | address _address; 12 | string _name; 13 | string _ipfs; 14 | string _docs; 15 | uint _added; 16 | } 17 | 18 | mapping(address => bool) public jobAdded; 19 | mapping(address => _job) public jobData; 20 | address[] public jobList; 21 | 22 | constructor() public { 23 | governance = msg.sender; 24 | } 25 | 26 | uint public length; 27 | 28 | function jobs() external view returns (address[] memory) { 29 | return jobList; 30 | } 31 | 32 | function job(address _address) external view returns (uint, address, string memory, string memory, string memory, uint) { 33 | _job memory __job = jobData[_address]; 34 | return (__job._id, __job._address, __job._name, __job._ipfs, __job._docs, __job._added); 35 | } 36 | 37 | function set(address _address, string calldata _name, string calldata _ipfs, string calldata _docs) external { 38 | require(msg.sender == governance, "Keep3rV1JobRegistry::add: !gov"); 39 | require(jobAdded[_address], "Keep3rV1JobRegistry::add: no job"); 40 | _job storage __job = jobData[_address]; 41 | 42 | __job._name = _name; 43 | __job._ipfs = _ipfs; 44 | __job._docs = _docs; 45 | 46 | } 47 | 48 | function add(address _address, string calldata _name, string calldata _ipfs, string calldata _docs) external { 49 | require(msg.sender == governance, "Keep3rV1JobRegistry::add: !gov"); 50 | require(!jobAdded[_address], "Keep3rV1JobRegistry::add: job exists"); 51 | jobAdded[_address] = true; 52 | jobList.push(_address); 53 | jobData[_address] = _job(length++, _address, _name, _ipfs, _docs, now); 54 | } 55 | 56 | /** 57 | * @notice Allows governance to change governance (for future upgradability) 58 | * @param _governance new governance address to set 59 | */ 60 | function setGovernance(address _governance) external { 61 | require(msg.sender == governance, "setGovernance: !gov"); 62 | pendingGovernance = _governance; 63 | } 64 | 65 | /** 66 | * @notice Allows pendingGovernance to accept their role as governance (protection pattern) 67 | */ 68 | function acceptGovernance() external { 69 | require(msg.sender == pendingGovernance, "acceptGovernance: !pendingGov"); 70 | governance = pendingGovernance; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /contracts/Keep3rV1Volatility.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.12; 4 | 5 | interface IKeep3rV1Oracle { 6 | function sample(address tokenIn, uint amountIn, address tokenOut, uint points, uint window) external view returns (uint[] memory); 7 | function current(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut); 8 | } 9 | 10 | interface IERC20 { 11 | function decimals() external view returns (uint); 12 | } 13 | 14 | contract Keep3rV1Volatility { 15 | 16 | uint private constant FIXED_1 = 0x080000000000000000000000000000000; 17 | uint private constant FIXED_2 = 0x100000000000000000000000000000000; 18 | uint private constant SQRT_1 = 13043817825332782212; 19 | uint private constant LNX = 3988425491; 20 | uint private constant LOG_10_2 = 3010299957; 21 | uint private constant LOG_E_2 = 6931471806; 22 | uint private constant BASE = 1e10; 23 | 24 | IKeep3rV1Oracle public constant KV1O = IKeep3rV1Oracle(0x73353801921417F465377c8d898c6f4C0270282C); 25 | address public constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 26 | 27 | function floorLog2(uint256 _n) public pure returns (uint8) { 28 | uint8 res = 0; 29 | 30 | if (_n < 256) { 31 | // At most 8 iterations 32 | while (_n > 1) { 33 | _n >>= 1; 34 | res += 1; 35 | } 36 | } else { 37 | // Exactly 8 iterations 38 | for (uint8 s = 128; s > 0; s >>= 1) { 39 | if (_n >= (uint(1) << s)) { 40 | _n >>= s; 41 | res |= s; 42 | } 43 | } 44 | } 45 | 46 | return res; 47 | } 48 | 49 | function ln(uint256 x) public pure returns (uint) { 50 | uint res = 0; 51 | 52 | // If x >= 2, then we compute the integer part of log2(x), which is larger than 0. 53 | if (x >= FIXED_2) { 54 | uint8 count = floorLog2(x / FIXED_1); 55 | x >>= count; // now x < 2 56 | res = count * FIXED_1; 57 | } 58 | 59 | // If x > 1, then we compute the fraction part of log2(x), which is larger than 0. 60 | if (x > FIXED_1) { 61 | for (uint8 i = 127; i > 0; --i) { 62 | x = (x * x) / FIXED_1; // now 1 < x < 4 63 | if (x >= FIXED_2) { 64 | x >>= 1; // now 1 < x < 2 65 | res += uint(1) << (i - 1); 66 | } 67 | } 68 | } 69 | 70 | return res * LOG_E_2 / BASE; 71 | } 72 | 73 | /** 74 | * @dev computes e ^ (x / FIXED_1) * FIXED_1 75 | * input range: 0 <= x <= OPT_EXP_MAX_VAL - 1 76 | * auto-generated via 'PrintFunctionOptimalExp.py' 77 | * Detailed description: 78 | * - Rewrite the input as a sum of binary exponents and a single residual r, as small as possible 79 | * - The exponentiation of each binary exponent is given (pre-calculated) 80 | * - The exponentiation of r is calculated via Taylor series for e^x, where x = r 81 | * - The exponentiation of the input is calculated by multiplying the intermediate results above 82 | * - For example: e^5.521692859 = e^(4 + 1 + 0.5 + 0.021692859) = e^4 * e^1 * e^0.5 * e^0.021692859 83 | */ 84 | function optimalExp(uint256 x) public pure returns (uint256) { 85 | uint256 res = 0; 86 | 87 | uint256 y; 88 | uint256 z; 89 | 90 | z = y = x % 0x10000000000000000000000000000000; // get the input modulo 2^(-3) 91 | z = (z * y) / FIXED_1; 92 | res += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!) 93 | z = (z * y) / FIXED_1; 94 | res += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!) 95 | z = (z * y) / FIXED_1; 96 | res += z * 0x0168244fdac78000; // add y^04 * (20! / 04!) 97 | z = (z * y) / FIXED_1; 98 | res += z * 0x004807432bc18000; // add y^05 * (20! / 05!) 99 | z = (z * y) / FIXED_1; 100 | res += z * 0x000c0135dca04000; // add y^06 * (20! / 06!) 101 | z = (z * y) / FIXED_1; 102 | res += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!) 103 | z = (z * y) / FIXED_1; 104 | res += z * 0x000036e0f639b800; // add y^08 * (20! / 08!) 105 | z = (z * y) / FIXED_1; 106 | res += z * 0x00000618fee9f800; // add y^09 * (20! / 09!) 107 | z = (z * y) / FIXED_1; 108 | res += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!) 109 | z = (z * y) / FIXED_1; 110 | res += z * 0x0000000e30dce400; // add y^11 * (20! / 11!) 111 | z = (z * y) / FIXED_1; 112 | res += z * 0x000000012ebd1300; // add y^12 * (20! / 12!) 113 | z = (z * y) / FIXED_1; 114 | res += z * 0x0000000017499f00; // add y^13 * (20! / 13!) 115 | z = (z * y) / FIXED_1; 116 | res += z * 0x0000000001a9d480; // add y^14 * (20! / 14!) 117 | z = (z * y) / FIXED_1; 118 | res += z * 0x00000000001c6380; // add y^15 * (20! / 15!) 119 | z = (z * y) / FIXED_1; 120 | res += z * 0x000000000001c638; // add y^16 * (20! / 16!) 121 | z = (z * y) / FIXED_1; 122 | res += z * 0x0000000000001ab8; // add y^17 * (20! / 17!) 123 | z = (z * y) / FIXED_1; 124 | res += z * 0x000000000000017c; // add y^18 * (20! / 18!) 125 | z = (z * y) / FIXED_1; 126 | res += z * 0x0000000000000014; // add y^19 * (20! / 19!) 127 | z = (z * y) / FIXED_1; 128 | res += z * 0x0000000000000001; // add y^20 * (20! / 20!) 129 | res = res / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0! 130 | 131 | if ((x & 0x010000000000000000000000000000000) != 0) 132 | res = (res * 0x1c3d6a24ed82218787d624d3e5eba95f9) / 0x18ebef9eac820ae8682b9793ac6d1e776; // multiply by e^2^(-3) 133 | if ((x & 0x020000000000000000000000000000000) != 0) 134 | res = (res * 0x18ebef9eac820ae8682b9793ac6d1e778) / 0x1368b2fc6f9609fe7aceb46aa619baed4; // multiply by e^2^(-2) 135 | if ((x & 0x040000000000000000000000000000000) != 0) 136 | res = (res * 0x1368b2fc6f9609fe7aceb46aa619baed5) / 0x0bc5ab1b16779be3575bd8f0520a9f21f; // multiply by e^2^(-1) 137 | if ((x & 0x080000000000000000000000000000000) != 0) 138 | res = (res * 0x0bc5ab1b16779be3575bd8f0520a9f21e) / 0x0454aaa8efe072e7f6ddbab84b40a55c9; // multiply by e^2^(+0) 139 | if ((x & 0x100000000000000000000000000000000) != 0) 140 | res = (res * 0x0454aaa8efe072e7f6ddbab84b40a55c5) / 0x00960aadc109e7a3bf4578099615711ea; // multiply by e^2^(+1) 141 | if ((x & 0x200000000000000000000000000000000) != 0) 142 | res = (res * 0x00960aadc109e7a3bf4578099615711d7) / 0x0002bf84208204f5977f9a8cf01fdce3d; // multiply by e^2^(+2) 143 | if ((x & 0x400000000000000000000000000000000) != 0) 144 | res = (res * 0x0002bf84208204f5977f9a8cf01fdc307) / 0x0000003c6ab775dd0b95b4cbee7e65d11; // multiply by e^2^(+3) 145 | 146 | return res; 147 | } 148 | 149 | function quote(address tokenIn, address tokenOut, uint t) public view returns (uint call, uint put) { 150 | uint _price = price(tokenIn, tokenOut); 151 | return quotePrice(tokenIn, tokenIn == WETH ? tokenOut : WETH, t, _price, _price); 152 | } 153 | 154 | function price(address tokenIn, address tokenOut) public view returns (uint) { 155 | if (tokenIn == WETH) { 156 | return KV1O.current(WETH, 1e18, tokenOut); 157 | } else { 158 | uint _weth = KV1O.current(tokenIn, uint(10)**IERC20(tokenIn).decimals(), WETH); 159 | if (tokenOut == WETH) { 160 | return _weth; 161 | } else { 162 | return KV1O.current(WETH, _weth, tokenOut); 163 | } 164 | } 165 | } 166 | 167 | function quotePrice(address tokenIn, address tokenOut, uint t, uint sp, uint st) public view returns (uint call, uint put) { 168 | uint v = rVol(tokenIn, tokenOut, 4, 24); 169 | return quoteAll(t, v, sp, st); 170 | } 171 | 172 | function quoteAll(uint t, uint v, uint sp, uint st) public pure returns (uint call, uint put) { 173 | uint _c; 174 | uint _p; 175 | 176 | if (sp > st) { 177 | _c = C(t, v, sp, st); 178 | _p = st-sp+_c; 179 | } else { 180 | _p = C(t, v, st, sp); 181 | _c = st-sp+_p; 182 | } 183 | return (_c, _p); 184 | } 185 | 186 | function C(uint t, uint v, uint sp, uint st) public pure returns (uint) { 187 | if (sp == st) { 188 | return LNX * sp / 1e10 * v / 1e18 * sqrt(1e18 * t / 365) / 1e9; 189 | } 190 | uint sigma = ((v**2)/2); 191 | uint sigmaB = 1e36; 192 | 193 | uint sig = 1e18 * sigma / sigmaB * t / 365; 194 | 195 | uint sSQRT = v * sqrt(1e18 * t / 365) / 1e9; 196 | 197 | uint d1 = 1e18 * ln(FIXED_1 * sp / st) / FIXED_1; 198 | d1 = (d1 + sig) * 1e18 / sSQRT; 199 | uint d2 = d1 - sSQRT; 200 | 201 | uint cdfD1 = ncdf(FIXED_1 * d1 / 1e18); 202 | uint cdfD2 = cdf(int(FIXED_1) * int(d2) / 1e18); 203 | 204 | return sp * cdfD1 / 1e14 - st * cdfD2 / 1e14; 205 | } 206 | 207 | function ncdf(uint x) public pure returns (uint) { 208 | int t1 = int(1e7 + (2315419 * x / FIXED_1)); 209 | uint exp = x / 2 * x / FIXED_1; 210 | int d = int(3989423 * FIXED_1 / optimalExp(uint(exp))); 211 | uint prob = uint(d * (3193815 + ( -3565638 + (17814780 + (-18212560 + 13302740 * 1e7 / t1) * 1e7 / t1) * 1e7 / t1) * 1e7 / t1) * 1e7 / t1); 212 | if( x > 0 ) prob = 1e14 - prob; 213 | return prob; 214 | } 215 | 216 | /** 217 | * @notice Takes the absolute value of a given number 218 | * @dev Helper function 219 | * @param _number The specified number 220 | * @return The absolute value of the number 221 | */ 222 | function abs(int256 _number) public pure returns (uint256) { 223 | return _number < 0 ? uint256(_number * (-1)) : uint256(_number); 224 | } 225 | 226 | function cdf(int x) public pure returns (uint) { 227 | int t1 = int(1e7 + int(2315419 * abs(x) / FIXED_1)); 228 | uint exp = uint(x / 2 * x) / FIXED_1; 229 | int d = int(3989423 * FIXED_1 / optimalExp(uint(exp))); 230 | uint prob = uint(d * (3193815 + ( -3565638 + (17814780 + (-18212560 + 13302740 * 1e7 / t1) * 1e7 / t1) * 1e7 / t1) * 1e7 / t1) * 1e7 / t1); 231 | if( x > 0 ) prob = 1e14 - prob; 232 | return prob; 233 | } 234 | 235 | function generalLog(uint256 x) public pure returns (uint) { 236 | uint res = 0; 237 | 238 | // If x >= 2, then we compute the integer part of log2(x), which is larger than 0. 239 | if (x >= FIXED_2) { 240 | uint8 count = floorLog2(x / FIXED_1); 241 | x >>= count; // now x < 2 242 | res = count * FIXED_1; 243 | } 244 | 245 | // If x > 1, then we compute the fraction part of log2(x), which is larger than 0. 246 | if (x > FIXED_1) { 247 | for (uint8 i = 127; i > 0; --i) { 248 | x = (x * x) / FIXED_1; // now 1 < x < 4 249 | if (x >= FIXED_2) { 250 | x >>= 1; // now 1 < x < 2 251 | res += uint(1) << (i - 1); 252 | } 253 | } 254 | } 255 | 256 | return res * LOG_10_2 / BASE; 257 | } 258 | 259 | function sqrt(uint x) public pure returns (uint y) { 260 | uint z = (x + 1) / 2; 261 | y = x; 262 | while (z < y) { 263 | y = z; 264 | z = (x / z + z) / 2; 265 | } 266 | } 267 | 268 | function vol(uint[] memory p) public pure returns (uint x) { 269 | for (uint8 i = 1; i <= (p.length-1); i++) { 270 | x += ((generalLog(p[i] * FIXED_1) - generalLog(p[i-1] * FIXED_1)))**2; 271 | //denom += FIXED_1**2; 272 | } 273 | //return (sum, denom); 274 | x = sqrt(uint(252) * sqrt(x / (p.length-1))); 275 | return uint(1e18) * x / SQRT_1; 276 | } 277 | 278 | function rVol(address tokenIn, address tokenOut, uint points, uint window) public view returns (uint) { 279 | return vol(KV1O.sample(tokenIn, uint(10)**IERC20(tokenIn).decimals(), tokenOut, points, window)); 280 | } 281 | 282 | function rVolHourly(address tokenIn, address tokenOut, uint points) external view returns (uint) { 283 | return rVol(tokenIn, tokenOut, points, 2); 284 | } 285 | 286 | function rVolDaily(address tokenIn, address tokenOut, uint points) external view returns (uint) { 287 | return rVol(tokenIn, tokenOut, points, 48); 288 | } 289 | 290 | function rVolWeekly(address tokenIn, address tokenOut, uint points) external view returns (uint) { 291 | return rVol(tokenIn, tokenOut, points, 336); 292 | } 293 | 294 | function rVolHourlyRecent(address tokenIn, address tokenOut) external view returns (uint) { 295 | return rVol(tokenIn, tokenOut, 2, 2); 296 | } 297 | 298 | function rVolDailyRecent(address tokenIn, address tokenOut) external view returns (uint) { 299 | return rVol(tokenIn, tokenOut, 2, 48); 300 | } 301 | 302 | function rVolWeeklyRecent(address tokenIn, address tokenOut) external view returns (uint) { 303 | return rVol(tokenIn, tokenOut, 2, 336); 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /contracts/Keep3rV2Helper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.2; 3 | 4 | library Math { 5 | function max(uint256 a, uint256 b) internal pure returns (uint256) { 6 | return a >= b ? a : b; 7 | } 8 | function min(uint256 a, uint256 b) internal pure returns (uint256) { 9 | return a < b ? a : b; 10 | } 11 | } 12 | 13 | interface IChainLinkFeed { 14 | function latestAnswer() external view returns (int256); 15 | } 16 | 17 | interface IKeep3rV1 { 18 | function totalBonded() external view returns (uint); 19 | function bonds(address keeper, address credit) external view returns (uint); 20 | function votes(address keeper) external view returns (uint); 21 | } 22 | 23 | interface IKeep3rV2Oracle { 24 | function quote(address tokenIn, uint amountIn, address tokenOut, uint points) external view returns (uint amountOut, uint lastUpdatedAgo); 25 | } 26 | 27 | contract Keep3rV2Helper { 28 | 29 | IChainLinkFeed public constant FASTGAS = IChainLinkFeed(0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C); 30 | IKeep3rV1 public constant KP3R = IKeep3rV1(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 31 | IKeep3rV2Oracle public constant KV2O = IKeep3rV2Oracle(0xe20B3f175F9f4e1EDDf333f96b72Bba138c9e83a); 32 | address public constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 33 | 34 | uint constant public MIN = 11; 35 | uint constant public MAX = 12; 36 | uint constant public BASE = 10; 37 | uint constant public SWAP = 300000; 38 | uint constant public TARGETBOND = 200e18; 39 | 40 | function quote(uint eth) public view returns (uint amountOut) { 41 | (amountOut,) = KV2O.quote(address(WETH), eth, address(KP3R), 1); 42 | } 43 | 44 | function getFastGas() external view returns (uint) { 45 | return uint(FASTGAS.latestAnswer()); 46 | } 47 | 48 | function bonds(address keeper) public view returns (uint) { 49 | return KP3R.bonds(keeper, address(KP3R)) + (KP3R.votes(keeper)); 50 | } 51 | 52 | function getQuoteLimitFor(address origin, uint gasUsed) public view returns (uint) { 53 | uint _quote = quote((gasUsed+SWAP)*(uint(FASTGAS.latestAnswer()))); 54 | uint _min = _quote * MIN / BASE; 55 | uint _boost = _quote * MAX / BASE; 56 | uint _bond = Math.min(bonds(origin), TARGETBOND); 57 | return Math.max(_min, _boost * _bond / TARGETBOND); 58 | } 59 | 60 | function getQuoteLimit(uint gasUsed) external view returns (uint) { 61 | return getQuoteLimitFor(tx.origin, gasUsed); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /contracts/Keep3rV2OracleFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.2; 3 | 4 | interface IUniswapV2Pair { 5 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 6 | function price0CumulativeLast() external view returns (uint); 7 | function price1CumulativeLast() external view returns (uint); 8 | function token0() external view returns (address); 9 | function token1() external view returns (address); 10 | } 11 | 12 | interface IKeep3rV1 { 13 | function keepers(address keeper) external returns (bool); 14 | function KPRH() external view returns (IKeep3rV1Helper); 15 | function receipt(address credit, address keeper, uint amount) external; 16 | } 17 | 18 | interface IKeep3rV1Helper { 19 | function getQuoteLimit(uint gasUsed) external view returns (uint); 20 | } 21 | 22 | // sliding oracle that uses observations collected to provide moving price averages in the past 23 | contract Keep3rV2Oracle { 24 | 25 | constructor(address _pair) { 26 | _factory = msg.sender; 27 | pair = _pair; 28 | (,,uint32 timestamp) = IUniswapV2Pair(_pair).getReserves(); 29 | uint112 _price0CumulativeLast = uint112(IUniswapV2Pair(_pair).price0CumulativeLast() * e10 / Q112); 30 | uint112 _price1CumulativeLast = uint112(IUniswapV2Pair(_pair).price1CumulativeLast() * e10 / Q112); 31 | observations[length++] = Observation(timestamp, _price0CumulativeLast, _price1CumulativeLast); 32 | } 33 | 34 | struct Observation { 35 | uint32 timestamp; 36 | uint112 price0Cumulative; 37 | uint112 price1Cumulative; 38 | } 39 | 40 | modifier factory() { 41 | require(msg.sender == _factory, "!F"); 42 | _; 43 | } 44 | 45 | Observation[65535] public observations; 46 | uint16 public length; 47 | 48 | address immutable _factory; 49 | address immutable public pair; 50 | // this is redundant with granularity and windowSize, but stored for gas savings & informational purposes. 51 | uint constant periodSize = 1800; 52 | uint Q112 = 2**112; 53 | uint e10 = 10**18; 54 | 55 | // Pre-cache slots for cheaper oracle writes 56 | function cache(uint size) external { 57 | uint _length = length+size; 58 | for (uint i = length; i < _length; i++) observations[i].timestamp = 1; 59 | } 60 | 61 | // update the current feed for free 62 | function update() external factory returns (bool) { 63 | return _update(); 64 | } 65 | 66 | function updateable() external view returns (bool) { 67 | Observation memory _point = observations[length-1]; 68 | (,, uint timestamp) = IUniswapV2Pair(pair).getReserves(); 69 | uint timeElapsed = timestamp - _point.timestamp; 70 | return timeElapsed > periodSize; 71 | } 72 | 73 | function _update() internal returns (bool) { 74 | Observation memory _point = observations[length-1]; 75 | (,, uint32 timestamp) = IUniswapV2Pair(pair).getReserves(); 76 | uint32 timeElapsed = timestamp - _point.timestamp; 77 | if (timeElapsed > periodSize) { 78 | uint112 _price0CumulativeLast = uint112(IUniswapV2Pair(pair).price0CumulativeLast() * e10 / Q112); 79 | uint112 _price1CumulativeLast = uint112(IUniswapV2Pair(pair).price1CumulativeLast() * e10 / Q112); 80 | observations[length++] = Observation(timestamp, _price0CumulativeLast, _price1CumulativeLast); 81 | return true; 82 | } 83 | return false; 84 | } 85 | 86 | function _computeAmountOut(uint start, uint end, uint elapsed, uint amountIn) internal view returns (uint amountOut) { 87 | amountOut = amountIn * (end - start) / e10 / elapsed; 88 | } 89 | 90 | function current(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut, uint lastUpdatedAgo) { 91 | (address token0,) = tokenIn < tokenOut ? (tokenIn, tokenOut) : (tokenOut, tokenIn); 92 | 93 | Observation memory _observation = observations[length-1]; 94 | uint price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast() * e10 / Q112; 95 | uint price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast() * e10 / Q112; 96 | (,,uint timestamp) = IUniswapV2Pair(pair).getReserves(); 97 | 98 | // Handle edge cases where we have no updates, will revert on first reading set 99 | if (timestamp == _observation.timestamp) { 100 | _observation = observations[length-2]; 101 | } 102 | 103 | uint timeElapsed = timestamp - _observation.timestamp; 104 | timeElapsed = timeElapsed == 0 ? 1 : timeElapsed; 105 | if (token0 == tokenIn) { 106 | amountOut = _computeAmountOut(_observation.price0Cumulative, price0Cumulative, timeElapsed, amountIn); 107 | } else { 108 | amountOut = _computeAmountOut(_observation.price1Cumulative, price1Cumulative, timeElapsed, amountIn); 109 | } 110 | lastUpdatedAgo = timeElapsed; 111 | } 112 | 113 | function quote(address tokenIn, uint amountIn, address tokenOut, uint points) external view returns (uint amountOut, uint lastUpdatedAgo) { 114 | (address token0,) = tokenIn < tokenOut ? (tokenIn, tokenOut) : (tokenOut, tokenIn); 115 | 116 | uint priceAverageCumulative = 0; 117 | uint _length = length-1; 118 | uint i = _length - points; 119 | Observation memory currentObservation; 120 | Observation memory nextObservation; 121 | 122 | uint nextIndex = 0; 123 | if (token0 == tokenIn) { 124 | for (; i < _length; i++) { 125 | nextIndex = i+1; 126 | currentObservation = observations[i]; 127 | nextObservation = observations[nextIndex]; 128 | priceAverageCumulative += _computeAmountOut( 129 | currentObservation.price0Cumulative, 130 | nextObservation.price0Cumulative, 131 | nextObservation.timestamp - currentObservation.timestamp, amountIn); 132 | } 133 | } else { 134 | for (; i < _length; i++) { 135 | nextIndex = i+1; 136 | currentObservation = observations[i]; 137 | nextObservation = observations[nextIndex]; 138 | priceAverageCumulative += _computeAmountOut( 139 | currentObservation.price1Cumulative, 140 | nextObservation.price1Cumulative, 141 | nextObservation.timestamp - currentObservation.timestamp, amountIn); 142 | } 143 | } 144 | amountOut = priceAverageCumulative / points; 145 | 146 | (,,uint timestamp) = IUniswapV2Pair(pair).getReserves(); 147 | lastUpdatedAgo = timestamp - nextObservation.timestamp; 148 | } 149 | 150 | function sample(address tokenIn, uint amountIn, address tokenOut, uint points, uint window) external view returns (uint[] memory prices, uint lastUpdatedAgo) { 151 | (address token0,) = tokenIn < tokenOut ? (tokenIn, tokenOut) : (tokenOut, tokenIn); 152 | prices = new uint[](points); 153 | 154 | if (token0 == tokenIn) { 155 | { 156 | uint _length = length-1; 157 | uint i = _length - (points * window); 158 | uint _index = 0; 159 | Observation memory nextObservation; 160 | for (; i < _length; i+=window) { 161 | Observation memory currentObservation; 162 | currentObservation = observations[i]; 163 | nextObservation = observations[i + window]; 164 | prices[_index] = _computeAmountOut( 165 | currentObservation.price0Cumulative, 166 | nextObservation.price0Cumulative, 167 | nextObservation.timestamp - currentObservation.timestamp, amountIn); 168 | _index = _index + 1; 169 | } 170 | 171 | (,,uint timestamp) = IUniswapV2Pair(pair).getReserves(); 172 | lastUpdatedAgo = timestamp - nextObservation.timestamp; 173 | } 174 | } else { 175 | { 176 | uint _length = length-1; 177 | uint i = _length - (points * window); 178 | uint _index = 0; 179 | Observation memory nextObservation; 180 | for (; i < _length; i+=window) { 181 | Observation memory currentObservation; 182 | currentObservation = observations[i]; 183 | nextObservation = observations[i + window]; 184 | prices[_index] = _computeAmountOut( 185 | currentObservation.price1Cumulative, 186 | nextObservation.price1Cumulative, 187 | nextObservation.timestamp - currentObservation.timestamp, amountIn); 188 | _index = _index + 1; 189 | } 190 | 191 | (,,uint timestamp) = IUniswapV2Pair(pair).getReserves(); 192 | lastUpdatedAgo = timestamp - nextObservation.timestamp; 193 | } 194 | } 195 | } 196 | } 197 | 198 | contract Keep3rV2OracleFactory { 199 | 200 | function pairForSushi(address tokenA, address tokenB) internal pure returns (address pair) { 201 | (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); 202 | pair = address(uint160(uint256(keccak256(abi.encodePacked( 203 | hex'ff', 204 | 0xc35DADB65012eC5796536bD9864eD8773aBc74C4, 205 | keccak256(abi.encodePacked(token0, token1)), 206 | hex'e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303' // init code hash 207 | ))))); 208 | } 209 | 210 | function pairForUni(address tokenA, address tokenB) internal pure returns (address pair) { 211 | (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); 212 | pair = address(uint160(uint256(keccak256(abi.encodePacked( 213 | hex'ff', 214 | 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f, 215 | keccak256(abi.encodePacked(token0, token1)), 216 | hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash 217 | ))))); 218 | } 219 | 220 | modifier keeper() { 221 | require(KP3R.keepers(msg.sender), "!K"); 222 | _; 223 | } 224 | 225 | modifier upkeep() { 226 | uint _gasUsed = gasleft(); 227 | require(KP3R.keepers(msg.sender), "!K"); 228 | _; 229 | uint _received = KP3R.KPRH().getQuoteLimit(_gasUsed - gasleft()); 230 | KP3R.receipt(address(KP3R), msg.sender, _received); 231 | } 232 | 233 | address public governance; 234 | address public pendingGovernance; 235 | 236 | /** 237 | * @notice Allows governance to change governance (for future upgradability) 238 | * @param _governance new governance address to set 239 | */ 240 | function setGovernance(address _governance) external { 241 | require(msg.sender == governance, "!G"); 242 | pendingGovernance = _governance; 243 | } 244 | 245 | /** 246 | * @notice Allows pendingGovernance to accept their role as governance (protection pattern) 247 | */ 248 | function acceptGovernance() external { 249 | require(msg.sender == pendingGovernance, "!pG"); 250 | governance = pendingGovernance; 251 | } 252 | 253 | IKeep3rV1 public constant KP3R = IKeep3rV1(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 254 | 255 | address[] internal _pairs; 256 | mapping(address => Keep3rV2Oracle) public feeds; 257 | 258 | function pairs() external view returns (address[] memory) { 259 | return _pairs; 260 | } 261 | 262 | constructor() { 263 | governance = msg.sender; 264 | } 265 | 266 | function update(address pair) external keeper returns (bool) { 267 | return feeds[pair].update(); 268 | } 269 | 270 | function byteCode(address pair) external pure returns (bytes memory bytecode) { 271 | bytecode = abi.encodePacked(type(Keep3rV2Oracle).creationCode, abi.encode(pair)); 272 | } 273 | 274 | function deploy(address pair) external returns (address feed) { 275 | require(msg.sender == governance, "!G"); 276 | require(address(feeds[pair]) == address(0), 'PE'); 277 | bytes memory bytecode = abi.encodePacked(type(Keep3rV2Oracle).creationCode, abi.encode(pair)); 278 | bytes32 salt = keccak256(abi.encodePacked(pair)); 279 | assembly { 280 | feed := create2(0, add(bytecode, 0x20), mload(bytecode), salt) 281 | if iszero(extcodesize(feed)) { 282 | revert(0, 0) 283 | } 284 | } 285 | feeds[pair] = Keep3rV2Oracle(feed); 286 | _pairs.push(pair); 287 | } 288 | 289 | function work() external upkeep { 290 | require(workable(), "!W"); 291 | for (uint i = 0; i < _pairs.length; i++) { 292 | feeds[_pairs[i]].update(); 293 | } 294 | } 295 | 296 | function work(address pair) external upkeep { 297 | require(feeds[pair].update(), "!W"); 298 | } 299 | 300 | function workForFree() external keeper { 301 | for (uint i = 0; i < _pairs.length; i++) { 302 | feeds[_pairs[i]].update(); 303 | } 304 | } 305 | 306 | function workForFree(address pair) external keeper { 307 | feeds[pair].update(); 308 | } 309 | 310 | function cache(uint size) external { 311 | for (uint i = 0; i < _pairs.length; i++) { 312 | feeds[_pairs[i]].cache(size); 313 | } 314 | } 315 | 316 | function cache(address pair, uint size) external { 317 | feeds[pair].cache(size); 318 | } 319 | 320 | function workable() public view returns (bool canWork) { 321 | canWork = true; 322 | for (uint i = 0; i < _pairs.length; i++) { 323 | if (!feeds[_pairs[i]].updateable()) { 324 | canWork = false; 325 | } 326 | } 327 | } 328 | 329 | function workable(address pair) public view returns (bool) { 330 | return feeds[pair].updateable(); 331 | } 332 | 333 | function sample(address tokenIn, uint amountIn, address tokenOut, uint points, uint window, bool sushiswap) external view returns (uint[] memory prices, uint lastUpdatedAgo) { 334 | address _pair = sushiswap ? pairForSushi(tokenIn, tokenOut) : pairForUni(tokenIn, tokenOut); 335 | return feeds[_pair].sample(tokenIn, amountIn, tokenOut, points, window); 336 | } 337 | 338 | function sample(address pair, address tokenIn, uint amountIn, address tokenOut, uint points, uint window) external view returns (uint[] memory prices, uint lastUpdatedAgo) { 339 | return feeds[pair].sample(tokenIn, amountIn, tokenOut, points, window); 340 | } 341 | 342 | function quote(address tokenIn, uint amountIn, address tokenOut, uint points, bool sushiswap) external view returns (uint amountOut, uint lastUpdatedAgo) { 343 | address _pair = sushiswap ? pairForSushi(tokenIn, tokenOut) : pairForUni(tokenIn, tokenOut); 344 | return feeds[_pair].quote(tokenIn, amountIn, tokenOut, points); 345 | } 346 | 347 | function quote(address pair, address tokenIn, uint amountIn, address tokenOut, uint points) external view returns (uint amountOut, uint lastUpdatedAgo) { 348 | return feeds[pair].quote(tokenIn, amountIn, tokenOut, points); 349 | } 350 | 351 | function current(address tokenIn, uint amountIn, address tokenOut, bool sushiswap) external view returns (uint amountOut, uint lastUpdatedAgo) { 352 | address _pair = sushiswap ? pairForSushi(tokenIn, tokenOut) : pairForUni(tokenIn, tokenOut); 353 | return feeds[_pair].current(tokenIn, amountIn, tokenOut); 354 | } 355 | 356 | function current(address pair, address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut, uint lastUpdatedAgo) { 357 | return feeds[pair].current(tokenIn, amountIn, tokenOut); 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /contracts/MetaKeep3r.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.17; 3 | 4 | library SafeMath { 5 | function add(uint a, uint b) internal pure returns (uint) { 6 | return add(a, b, "add: +") 7 | } 8 | function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 9 | uint c = a + b; 10 | require(c >= a, errorMessage); 11 | 12 | return c; 13 | } 14 | function sub(uint a, uint b) internal pure returns (uint) { 15 | return sub(a, b, "sub: -"); 16 | } 17 | function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 18 | require(b <= a, errorMessage); 19 | uint c = a - b; 20 | 21 | return c; 22 | } 23 | } 24 | 25 | interface IKeep3rV1 { 26 | function isMinKeeper(address keeper, uint minBond, uint earned, uint age) external returns (bool); 27 | function receipt(address credit, address keeper, uint amount) external; 28 | function unbond(address bonding, uint amount) external; 29 | function withdraw(address bonding) external; 30 | function bonds(address keeper, address credit) external view returns (uint); 31 | function unbondings(address keeper, address credit) external view returns (uint); 32 | function approve(address spender, uint amount) external returns (bool); 33 | function jobs(address job) external view returns (bool); 34 | function balanceOf(address account) external view returns (uint256); 35 | } 36 | 37 | interface WETH9 { 38 | function withdraw(uint wad) external; 39 | } 40 | 41 | interface IUniswapV2Router { 42 | function swapExactTokensForTokens( 43 | uint amountIn, 44 | uint amountOutMin, 45 | address[] calldata path, 46 | address to, 47 | uint deadline 48 | ) external returns (uint[] memory amounts); 49 | } 50 | 51 | interface IKeep3rJob { 52 | function work() external; 53 | } 54 | 55 | contract MetaKeep3r { 56 | using SafeMath for uint; 57 | 58 | modifier upkeep() { 59 | require(KP3R.isMinKeeper(msg.sender, 100e18, 0, 0), "MetaKeep3r::isKeeper: keeper is not registered"); 60 | uint _before = KP3R.bonds(address(this), address(KP3R)); 61 | _; 62 | uint _after = KP3R.bonds(address(this), address(KP3R)); 63 | uint _received = _after.sub(_before); 64 | uint _balance = KP3R.balanceOf(address(this)); 65 | if (_balance < _received) { 66 | KP3R.receipt(address(KP3R), address(this), _received.sub(_balance)); 67 | } 68 | _received = _swap(_received); 69 | msg.sender.transfer(_received); 70 | } 71 | 72 | function task(address job, bytes calldata data) external upkeep { 73 | require(KP3R.jobs(job), "MetaKeep3r::work: invalid job"); 74 | (bool success,) = job.call.value(0)(data); 75 | require(success, "MetaKeep3r::work: job failure"); 76 | } 77 | 78 | function work(address job) external upkeep { 79 | require(KP3R.jobs(job), "MetaKeep3r::work: invalid job"); 80 | IKeep3rJob(job).work(); 81 | } 82 | 83 | IKeep3rV1 public constant KP3R = IKeep3rV1(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 84 | WETH9 public constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 85 | IUniswapV2Router public constant UNI = IUniswapV2Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 86 | 87 | function unbond() external { 88 | require(KP3R.unbondings(address(this), address(KP3R)) < now, "MetaKeep3r::unbond: unbonding"); 89 | KP3R.unbond(address(KP3R), KP3R.bonds(address(this), address(KP3R))); 90 | } 91 | 92 | function withdraw() external { 93 | KP3R.withdraw(address(KP3R)); 94 | KP3R.unbond(address(KP3R), KP3R.bonds(address(this), address(KP3R))); 95 | } 96 | 97 | function() external payable {} 98 | 99 | function _swap(uint _amount) internal returns (uint) { 100 | KP3R.approve(address(UNI), _amount); 101 | 102 | address[] memory path = new address[](2); 103 | path[0] = address(KP3R); 104 | path[1] = address(WETH); 105 | 106 | uint[] memory amounts = UNI.swapExactTokensForTokens(_amount, uint256(0), path, address(this), now.add(1800)); 107 | WETH.withdraw(amounts[1]); 108 | return amounts[1]; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.8.0; 3 | 4 | contract Migrations { 5 | address public owner = msg.sender; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | require( 10 | msg.sender == owner, 11 | "This function is restricted to the contract's owner" 12 | ); 13 | _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/jobs/AaveLiquidateKeep3r.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at Etherscan.io on 2020-10-28 3 | */ 4 | 5 | // SPDX-License-Identifier: MIT 6 | pragma solidity ^0.6.12; 7 | 8 | 9 | /** 10 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 11 | * checks. 12 | * 13 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 14 | * in bugs, because programmers usually assume that an overflow raises an 15 | * error, which is the standard behavior in high level programming languages. 16 | * `SafeMath` restores this intuition by reverting the transaction when an 17 | * operation overflows. 18 | * 19 | * Using this library instead of the unchecked operations eliminates an entire 20 | * class of bugs, so it's recommended to use it always. 21 | */ 22 | library SafeMath { 23 | /** 24 | * @dev Returns the addition of two unsigned integers, reverting on overflow. 25 | * 26 | * Counterpart to Solidity's `+` operator. 27 | * 28 | * Requirements: 29 | * - Addition cannot overflow. 30 | */ 31 | function add(uint a, uint b) internal pure returns (uint) { 32 | uint c = a + b; 33 | require(c >= a, "add: +"); 34 | 35 | return c; 36 | } 37 | 38 | /** 39 | * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow. 40 | * 41 | * Counterpart to Solidity's `+` operator. 42 | * 43 | * Requirements: 44 | * - Addition cannot overflow. 45 | */ 46 | function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 47 | uint c = a + b; 48 | require(c >= a, errorMessage); 49 | 50 | return c; 51 | } 52 | 53 | /** 54 | * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). 55 | * 56 | * Counterpart to Solidity's `-` operator. 57 | * 58 | * Requirements: 59 | * - Subtraction cannot underflow. 60 | */ 61 | function sub(uint a, uint b) internal pure returns (uint) { 62 | return sub(a, b, "sub: -"); 63 | } 64 | 65 | /** 66 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). 67 | * 68 | * Counterpart to Solidity's `-` operator. 69 | * 70 | * Requirements: 71 | * - Subtraction cannot underflow. 72 | */ 73 | function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 74 | require(b <= a, errorMessage); 75 | uint c = a - b; 76 | 77 | return c; 78 | } 79 | 80 | /** 81 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 82 | * 83 | * Counterpart to Solidity's `*` operator. 84 | * 85 | * Requirements: 86 | * - Multiplication cannot overflow. 87 | */ 88 | function mul(uint a, uint b) internal pure returns (uint) { 89 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 90 | // benefit is lost if 'b' is also tested. 91 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 92 | if (a == 0) { 93 | return 0; 94 | } 95 | 96 | uint c = a * b; 97 | require(c / a == b, "mul: *"); 98 | 99 | return c; 100 | } 101 | 102 | /** 103 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 104 | * 105 | * Counterpart to Solidity's `*` operator. 106 | * 107 | * Requirements: 108 | * - Multiplication cannot overflow. 109 | */ 110 | function mul(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 111 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 112 | // benefit is lost if 'b' is also tested. 113 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 114 | if (a == 0) { 115 | return 0; 116 | } 117 | 118 | uint c = a * b; 119 | require(c / a == b, errorMessage); 120 | 121 | return c; 122 | } 123 | 124 | /** 125 | * @dev Returns the integer division of two unsigned integers. 126 | * Reverts on division by zero. The result is rounded towards zero. 127 | * 128 | * Counterpart to Solidity's `/` operator. Note: this function uses a 129 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 130 | * uses an invalid opcode to revert (consuming all remaining gas). 131 | * 132 | * Requirements: 133 | * - The divisor cannot be zero. 134 | */ 135 | function div(uint a, uint b) internal pure returns (uint) { 136 | return div(a, b, "div: /"); 137 | } 138 | 139 | /** 140 | * @dev Returns the integer division of two unsigned integers. 141 | * Reverts with custom message on division by zero. The result is rounded towards zero. 142 | * 143 | * Counterpart to Solidity's `/` operator. Note: this function uses a 144 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 145 | * uses an invalid opcode to revert (consuming all remaining gas). 146 | * 147 | * Requirements: 148 | * - The divisor cannot be zero. 149 | */ 150 | function div(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 151 | // Solidity only automatically asserts when dividing by 0 152 | require(b > 0, errorMessage); 153 | uint c = a / b; 154 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 155 | 156 | return c; 157 | } 158 | 159 | /** 160 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 161 | * Reverts when dividing by zero. 162 | * 163 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 164 | * opcode (which leaves remaining gas untouched) while Solidity uses an 165 | * invalid opcode to revert (consuming all remaining gas). 166 | * 167 | * Requirements: 168 | * - The divisor cannot be zero. 169 | */ 170 | function mod(uint a, uint b) internal pure returns (uint) { 171 | return mod(a, b, "mod: %"); 172 | } 173 | 174 | /** 175 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 176 | * Reverts with custom message when dividing by zero. 177 | * 178 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 179 | * opcode (which leaves remaining gas untouched) while Solidity uses an 180 | * invalid opcode to revert (consuming all remaining gas). 181 | * 182 | * Requirements: 183 | * - The divisor cannot be zero. 184 | */ 185 | function mod(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 186 | require(b != 0, errorMessage); 187 | return a % b; 188 | } 189 | } 190 | 191 | /** 192 | * @dev Interface of the ERC20 standard as defined in the EIP. 193 | */ 194 | interface IERC20 { 195 | /** 196 | * @dev Returns the amount of tokens in existence. 197 | */ 198 | function totalSupply() external view returns (uint256); 199 | 200 | /** 201 | * @dev Returns the amount of tokens owned by `account`. 202 | */ 203 | function balanceOf(address account) external view returns (uint256); 204 | 205 | /** 206 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 207 | * 208 | * Returns a boolean value indicating whether the operation succeeded. 209 | * 210 | * Emits a {Transfer} event. 211 | */ 212 | function transfer(address recipient, uint256 amount) external returns (bool); 213 | 214 | /** 215 | * @dev Returns the remaining number of tokens that `spender` will be 216 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 217 | * zero by default. 218 | * 219 | * This value changes when {approve} or {transferFrom} are called. 220 | */ 221 | function allowance(address owner, address spender) external view returns (uint256); 222 | 223 | /** 224 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 225 | * 226 | * Returns a boolean value indicating whether the operation succeeded. 227 | * 228 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 229 | * that someone may use both the old and the new allowance by unfortunate 230 | * transaction ordering. One possible solution to mitigate this race 231 | * condition is to first reduce the spender's allowance to 0 and set the 232 | * desired value afterwards: 233 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 234 | * 235 | * Emits an {Approval} event. 236 | */ 237 | function approve(address spender, uint256 amount) external returns (bool); 238 | 239 | /** 240 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 241 | * allowance mechanism. `amount` is then deducted from the caller's 242 | * allowance. 243 | * 244 | * Returns a boolean value indicating whether the operation succeeded. 245 | * 246 | * Emits a {Transfer} event. 247 | */ 248 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 249 | 250 | /** 251 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 252 | * another (`to`). 253 | * 254 | * Note that `value` may be zero. 255 | */ 256 | event Transfer(address indexed from, address indexed to, uint256 value); 257 | 258 | /** 259 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 260 | * a call to {approve}. `value` is the new allowance. 261 | */ 262 | event Approval(address indexed owner, address indexed spender, uint256 value); 263 | } 264 | 265 | /** 266 | * @dev Collection of functions related to the address type 267 | */ 268 | library Address { 269 | /** 270 | * @dev Returns true if `account` is a contract. 271 | * 272 | * [IMPORTANT] 273 | * ==== 274 | * It is unsafe to assume that an address for which this function returns 275 | * false is an externally-owned account (EOA) and not a contract. 276 | * 277 | * Among others, `isContract` will return false for the following 278 | * types of addresses: 279 | * 280 | * - an externally-owned account 281 | * - a contract in construction 282 | * - an address where a contract will be created 283 | * - an address where a contract lived, but was destroyed 284 | * ==== 285 | */ 286 | function isContract(address account) internal view returns (bool) { 287 | // According to EIP-1052, 0x0 is the value returned for not-yet created accounts 288 | // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned 289 | // for accounts without code, i.e. `keccak256('')` 290 | bytes32 codehash; 291 | bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; 292 | // solhint-disable-next-line no-inline-assembly 293 | assembly { codehash := extcodehash(account) } 294 | return (codehash != accountHash && codehash != 0x0); 295 | } 296 | 297 | /** 298 | * @dev Converts an `address` into `address payable`. Note that this is 299 | * simply a type cast: the actual underlying value is not changed. 300 | * 301 | * _Available since v2.4.0._ 302 | */ 303 | function toPayable(address account) internal pure returns (address payable) { 304 | return address(uint160(account)); 305 | } 306 | 307 | /** 308 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 309 | * `recipient`, forwarding all available gas and reverting on errors. 310 | * 311 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 312 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 313 | * imposed by `transfer`, making them unable to receive funds via 314 | * `transfer`. {sendValue} removes this limitation. 315 | * 316 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 317 | * 318 | * IMPORTANT: because control is transferred to `recipient`, care must be 319 | * taken to not create reentrancy vulnerabilities. Consider using 320 | * {ReentrancyGuard} or the 321 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 322 | * 323 | * _Available since v2.4.0._ 324 | */ 325 | function sendValue(address payable recipient, uint256 amount) internal { 326 | require(address(this).balance >= amount, "Address: insufficient"); 327 | 328 | // solhint-disable-next-line avoid-call-value 329 | (bool success, ) = recipient.call{value:amount}(""); 330 | require(success, "Address: reverted"); 331 | } 332 | } 333 | 334 | /** 335 | * @title SafeERC20 336 | * @dev Wrappers around ERC20 operations that throw on failure (when the token 337 | * contract returns false). Tokens that return no value (and instead revert or 338 | * throw on failure) are also supported, non-reverting calls are assumed to be 339 | * successful. 340 | * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, 341 | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. 342 | */ 343 | library SafeERC20 { 344 | using SafeMath for uint256; 345 | using Address for address; 346 | 347 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 348 | callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 349 | } 350 | 351 | function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { 352 | callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 353 | } 354 | 355 | function safeApprove(IERC20 token, address spender, uint256 value) internal { 356 | // safeApprove should only be called when setting an initial allowance, 357 | // or when resetting it to zero. To increase and decrease it, use 358 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 359 | // solhint-disable-next-line max-line-length 360 | require((value == 0) || (token.allowance(address(this), spender) == 0), 361 | "SafeERC20: approve from non-zero to non-zero allowance" 362 | ); 363 | callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); 364 | } 365 | 366 | function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { 367 | uint256 newAllowance = token.allowance(address(this), spender).add(value); 368 | callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 369 | } 370 | 371 | function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { 372 | uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: < 0"); 373 | callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 374 | } 375 | 376 | /** 377 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement 378 | * on the return value: the return value is optional (but if data is returned, it must not be false). 379 | * @param token The token targeted by the call. 380 | * @param data The call data (encoded using abi.encode or one of its variants). 381 | */ 382 | function callOptionalReturn(IERC20 token, bytes memory data) private { 383 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since 384 | // we're implementing it ourselves. 385 | 386 | // A Solidity high level call has three parts: 387 | // 1. The target address is checked to verify it contains contract code 388 | // 2. The call itself is made, and success asserted 389 | // 3. The return value is decoded, which in turn checks the size of the returned data. 390 | // solhint-disable-next-line max-line-length 391 | require(address(token).isContract(), "SafeERC20: !contract"); 392 | 393 | // solhint-disable-next-line avoid-low-level-calls 394 | (bool success, bytes memory returndata) = address(token).call(data); 395 | require(success, "SafeERC20: low-level call failed"); 396 | 397 | if (returndata.length > 0) { // Return data is optional 398 | // solhint-disable-next-line max-line-length 399 | require(abi.decode(returndata, (bool)), "SafeERC20: !succeed"); 400 | } 401 | } 402 | } 403 | 404 | interface IAave { 405 | function liquidationCall( 406 | address _collateral, 407 | address _reserve, 408 | address _user, 409 | uint256 _purchaseAmount, 410 | bool _receiveAToken 411 | ) external payable; 412 | function getUserReserveData(address _reserve, address _user) 413 | external 414 | view 415 | returns ( 416 | uint256 currentATokenBalance, 417 | uint256 currentBorrowBalance, 418 | uint256 principalBorrowBalance, 419 | uint256 borrowRateMode, 420 | uint256 borrowRate, 421 | uint256 liquidityRate, 422 | uint256 originationFee, 423 | uint256 variableBorrowIndex, 424 | uint256 lastUpdateTimestamp, 425 | bool usageAsCollateralEnabled 426 | ); 427 | } 428 | 429 | interface IKeep3rV1 { 430 | function isKeeper(address) external returns (bool); 431 | function worked(address keeper) external; 432 | } 433 | 434 | contract AaveLiquidate { 435 | using SafeERC20 for IERC20; 436 | 437 | IAave constant public AAVE = IAave(0x398eC7346DcD622eDc5ae82352F02bE94C62d119); 438 | address constant public CORE = address(0x3dfd23A6c5E8BbcFc9581d2E864a68feb6a076d3); 439 | 440 | function getUserReserveBalance(address _reserve, address _user) public view returns (uint256) { 441 | (,uint currentBorrowBalance,,,,,,,,) = AAVE.getUserReserveData(_reserve, _user); 442 | return currentBorrowBalance; 443 | } 444 | 445 | modifier upkeep() { 446 | require(KP3R.isKeeper(msg.sender), "::isKeeper: keeper is not registered"); 447 | _; 448 | KP3R.worked(msg.sender); 449 | } 450 | 451 | IKeep3rV1 public constant KP3R = IKeep3rV1(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 452 | 453 | function liquidate(address _collateral, address _reserve, address _user) external upkeep { 454 | uint256 _repay = getUserReserveBalance(_reserve, _user); 455 | require(_repay > 0, "No debt"); 456 | 457 | IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _repay); 458 | IERC20(_reserve).safeApprove(CORE, _repay); 459 | AAVE.liquidationCall(_collateral, _reserve, _user, _repay, false); 460 | IERC20(_reserve).safeApprove(CORE, 0); 461 | 462 | uint256 _liquidated = IERC20(_collateral).balanceOf(address(this)); 463 | require(_liquidated > 0, "Failed to liquidate"); 464 | 465 | IERC20(_reserve).safeTransfer(msg.sender, IERC20(_reserve).balanceOf(address(this))); 466 | IERC20(_collateral).safeTransfer(msg.sender, IERC20(_collateral).balanceOf(address(this))); 467 | } 468 | } 469 | -------------------------------------------------------------------------------- /contracts/jobs/HegicPoolKeep3r.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.8; 4 | 5 | import '@openzeppelin/contracts/math/SafeMath.sol'; 6 | 7 | import '../../interfaces/HegicPool/IHegicPoolV2.sol'; 8 | import '../../interfaces/Keep3r/IHegicPoolKeep3r.sol'; 9 | import '../../interfaces/LotManager/ILotManager.sol'; 10 | import '../../interfaces/IHegicStaking.sol'; 11 | 12 | import '../Governable.sol'; 13 | import '../CollectableDust.sol'; 14 | 15 | import './Keep3rAbstract.sol'; 16 | 17 | contract HegicPoolKeep3r is Governable, CollectableDust, Keep3r, IHegicPoolKeep3r { 18 | using SafeMath for uint256; 19 | 20 | uint256 public minETHRewards = 0; 21 | uint256 public minWBTCRewards = 0; 22 | IHegicPoolV2 public HegicPool; 23 | 24 | constructor( 25 | address _keep3r, 26 | address _hegicPool, 27 | uint256 _minETHRewards, 28 | uint256 _minWBTCRewards 29 | ) public Governable(msg.sender) CollectableDust() Keep3r(_keep3r) { 30 | setHegicPool(_hegicPool); 31 | setMinRewards(_minETHRewards, _minWBTCRewards); 32 | } 33 | 34 | // Setters 35 | function setHegicPool(address _hegicPool) public override onlyGovernor { 36 | require(IHegicPoolV2(_hegicPool).isHegicPool(), 'pool-keeper::set-hegic-pool:not-hegic-pool'); 37 | HegicPool = IHegicPoolV2(_hegicPool); 38 | emit HegicPoolSet(_hegicPool); 39 | } 40 | 41 | function setMinRewards(uint256 _minETHRewards, uint256 _minWBTCRewards) public override onlyGovernor { 42 | minETHRewards = _minETHRewards; 43 | minWBTCRewards = _minWBTCRewards; 44 | emit MinRewardsSet(minETHRewards, minWBTCRewards); 45 | } 46 | 47 | function setKeep3r(address _keep3r) public override onlyGovernor { 48 | _setKeep3r(_keep3r); 49 | emit Keep3rSet(_keep3r); 50 | } 51 | 52 | // Keep3r actions 53 | function claimRewards() external override paysKeeper { 54 | require(workable(), 'pool-keeper:claimRewards::not-enough-profit'); 55 | uint256 _rewards = HegicPool.claimRewards(); 56 | emit RewardsClaimedByKeeper(_rewards); 57 | } 58 | 59 | // Governor keeper bypass 60 | function workable() public view override returns (bool) { 61 | ILotManager LotManager = ILotManager(HegicPool.getLotManager()); 62 | IHegicStaking HegicStakingETH = LotManager.hegicStakingETH(); 63 | IHegicStaking HegicStakingWBTC = LotManager.hegicStakingWBTC(); 64 | 65 | return ( 66 | HegicStakingETH.profitOf(address(LotManager)) >= minETHRewards || 67 | HegicStakingWBTC.profitOf(address(LotManager)) >= minWBTCRewards 68 | ); 69 | } 70 | 71 | function forceClaimRewards() external override onlyGovernor { 72 | uint256 _rewards = HegicPool.claimRewards(); 73 | emit ForcedClaimedRewards(_rewards); 74 | } 75 | 76 | // HegicPool Manager 77 | function buyLots(uint256 _eth, uint256 _wbtc) external override onlyGovernor { 78 | HegicPool.buyLots(_eth, _wbtc); 79 | emit LotsBought(_eth, _wbtc); 80 | } 81 | 82 | function setPendingManager(address _pendingManager) external override onlyGovernor { 83 | HegicPool.setPendingManager(_pendingManager); 84 | emit PendingManagerSet(_pendingManager); 85 | } 86 | 87 | function acceptManager() external override onlyGovernor { 88 | HegicPool.acceptManager(); 89 | emit ManagerAccepted(); 90 | } 91 | 92 | // Governable 93 | function setPendingGovernor(address _pendingGovernor) external override onlyGovernor { 94 | _setPendingGovernor(_pendingGovernor); 95 | } 96 | 97 | function acceptGovernor() external override onlyPendingGovernor { 98 | _acceptGovernor(); 99 | } 100 | 101 | // Collectable Dust 102 | function sendDust( 103 | address _to, 104 | address _token, 105 | uint256 _amount 106 | ) external override onlyGovernor { 107 | _sendDust(_to, _token, _amount); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /contracts/jobs/SushiswapV2Keep3r.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.17; 3 | 4 | interface IKeep3rV1 { 5 | function isKeeper(address) external returns (bool); 6 | function worked(address keeper) external; 7 | } 8 | 9 | interface ISushiswapV2Factory { 10 | function allPairsLength() external view returns (uint); 11 | function allPairs(uint) external view returns (address pair); 12 | } 13 | 14 | interface ISushiswapV2Pair { 15 | function token0() external view returns (address); 16 | function token1() external view returns (address); 17 | function balanceOf(address account) external view returns (uint); 18 | } 19 | 20 | interface ISushiswapV2Maker { 21 | function convert(address token0, address token1) external; 22 | } 23 | 24 | contract SushiswapV2Keep3r { 25 | 26 | modifier upkeep() { 27 | require(KP3R.isKeeper(msg.sender), "SushiswapV2Keep3r::isKeeper: keeper is not registered"); 28 | _; 29 | KP3R.worked(msg.sender); 30 | } 31 | 32 | IKeep3rV1 public constant KP3R = IKeep3rV1(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 33 | ISushiswapV2Factory public constant SV2F = ISushiswapV2Factory(0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac); 34 | ISushiswapV2Maker public constant SV2M = ISushiswapV2Maker(0x6684977bBED67e101BB80Fc07fCcfba655c0a64F); 35 | 36 | function count() public view returns (uint) { 37 | uint _count = 0; 38 | for (uint i = 0; i < SV2F.allPairsLength(); i++) { 39 | if (haveBalance(SV2F.allPairs(i))) { 40 | _count++; 41 | } 42 | } 43 | return _count; 44 | } 45 | 46 | function workableAll(uint _count) external view returns (address[] memory) { 47 | return (workable(_count, 0, SV2F.allPairsLength())); 48 | } 49 | 50 | function workable(uint _count, uint start, uint end) public view returns (address[] memory) { 51 | address[] memory _workable = new address[](_count); 52 | uint index = 0; 53 | for (uint i = start; i < end; i++) { 54 | if (haveBalance(SV2F.allPairs(i))) { 55 | _workable[index] = SV2F.allPairs(i); 56 | index++; 57 | } 58 | } 59 | return _workable; 60 | } 61 | 62 | function haveBalance(address pair) public view returns (bool) { 63 | return ISushiswapV2Pair(pair).balanceOf(address(SV2M)) > 0; 64 | } 65 | 66 | function batch(ISushiswapV2Pair[] calldata pair) external { 67 | bool _worked = true; 68 | for (uint i = 0; i < pair.length; i++) { 69 | if (haveBalance(address(pair[i]))) { 70 | (bool success, bytes memory message) = address(SV2M).delegatecall(abi.encodeWithSignature("convert(address,address)", pair[i].token0(), pair[i].token1())); 71 | require(success, string(abi.encodePacked("SushiswapV2Keep3r::convert: failed [", message, "]"))); 72 | } else { 73 | _worked = false; 74 | } 75 | } 76 | require(_worked, "SushiswapV2Keep3r::batch: job(s) failed"); 77 | } 78 | 79 | function work(ISushiswapV2Pair pair) external { 80 | require(haveBalance(address(pair)), "SushiswapV2Keep3r::work: invalid pair"); 81 | (bool success, bytes memory message) = address(SV2M).delegatecall(abi.encodeWithSignature("convert(address,address)", pair.token0(), pair.token1())); 82 | require(success, string(abi.encodePacked("SushiswapV2Keep3r::convert: failed [", message, "]"))); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /contracts/jobs/SynthetixKeep3r.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.12; 3 | 4 | interface IKeep3rV1 { 5 | function isKeeper(address) external returns (bool); 6 | function worked(address) external; 7 | } 8 | 9 | interface ISynthetixFeePool { 10 | function closeCurrentFeePeriod() external; 11 | } 12 | 13 | interface ISynthetixExchangeRates { 14 | function freezeRate(bytes32) external; 15 | } 16 | 17 | interface ISynthetixEtherCollateralsUSD { 18 | function liquidateLoan(address, uint, uint) external; 19 | } 20 | 21 | interface ISynthetix { 22 | function liquidateDelinquentAccount(address, uint) external returns (bool); 23 | } 24 | 25 | interface ISynthetixLiquidations { 26 | function flagAccountForLiquidation(address) external; 27 | } 28 | 29 | contract SynthetixKeep3rV1 { 30 | 31 | IKeep3rV1 constant public KP3R = IKeep3rV1(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 32 | 33 | ISynthetixFeePool constant public SFP = ISynthetixFeePool(0xb440DD674e1243644791a4AdfE3A2AbB0A92d309); // ISynthetixFeePool(0x013D16CB1Bd493bBB89D45b43254842FadC169C8); 34 | ISynthetixExchangeRates constant public SER = ISynthetixExchangeRates(0xda80E6024bC82C9fe9e4e6760a9769CF0D231E80); 35 | ISynthetixEtherCollateralsUSD constant public SECS = ISynthetixEtherCollateralsUSD(0xfED77055B40d63DCf17ab250FFD6948FBFF57B82); 36 | ISynthetix constant public SNX = ISynthetix(0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F); // ISynthetix(0x6eB3aC83701f624bAEfBc50db654b53d1F51dC94); 37 | ISynthetixLiquidations constant public SL = ISynthetixLiquidations(0x46338723022deF2c5151e83BE759796A988754a2); 38 | 39 | 40 | modifier keeper() { 41 | require(KP3R.isKeeper(msg.sender), "::isKeeper: keeper is not registered"); 42 | _; 43 | } 44 | 45 | modifier upkeep() { 46 | require(KP3R.isKeeper(msg.sender), "::isKeeper: keeper is not registered"); 47 | _; 48 | KP3R.worked(msg.sender); 49 | } 50 | 51 | function closeCurrentFeePeriod() external upkeep { 52 | // SFP.closeCurrentFeePeriod(); 53 | (bool success,) = address(SFP).delegatecall(abi.encodeWithSignature("closeCurrentFeePeriod()")); 54 | require(success, "SynthetixKeep3rV1::closeCurrentFeePeriod: failed"); 55 | } 56 | 57 | /// @notice uses msg.sender in sub call 58 | function flagAccountForLiquidation(address account) external upkeep { 59 | // SL.flagAccountForLiquidation(account); 60 | (bool success,) = address(SL).delegatecall(abi.encodeWithSignature("flagAccountForLiquidation(address)", account)); 61 | require(success, "SynthetixKeep3rV1::flagAccountForLiquidation: failed"); 62 | } 63 | 64 | /// @notice uses messageSender in sub call 65 | function liquidateDelinquentAccount(address account, uint susdAmount) external upkeep { 66 | // SNX.liquidateDelinquentAccount(account, susdAmount); 67 | (bool success,) = address(SNX).delegatecall(abi.encodeWithSignature("liquidateDelinquentAccount(address,uint)", account, susdAmount)); 68 | require(success, "SynthetixKeep3rV1::liquidateDelinquentAccount: failed"); 69 | } 70 | 71 | /// @notice uses msg.sender in sub call 72 | function liquidateLoan(address _loanCreatorsAddress, uint256 _loanID, uint256 _debtToCover) external upkeep { 73 | // SECS.liquidateLoan(_loanCreatorsAddress, _loanID, _debtToCover); 74 | (bool success,) = address(SECS).delegatecall(abi.encodeWithSignature("liquidateLoan(address,uint,uint)", _loanCreatorsAddress, _loanID, _debtToCover)); 75 | require(success, "SynthetixKeep3rV1::liquidateLoan: failed"); 76 | } 77 | 78 | /// @notice uses msg.sender in sub call 79 | function freezeRate(bytes32 currencyKey) external upkeep { 80 | // SER.freezeRate(currencyKey); 81 | (bool success,) = address(SER).delegatecall(abi.encodeWithSignature("freezeRate(bytes32)", currencyKey)); 82 | require(success, "SynthetixKeep3rV1::freezeRate: failed"); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /contracts/jobs/YearnV1EarnKeep3r.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.12; 3 | 4 | 5 | /** 6 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 7 | * checks. 8 | * 9 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 10 | * in bugs, because programmers usually assume that an overflow raises an 11 | * error, which is the standard behavior in high level programming languages. 12 | * `SafeMath` restores this intuition by reverting the transaction when an 13 | * operation overflows. 14 | * 15 | * Using this library instead of the unchecked operations eliminates an entire 16 | * class of bugs, so it's recommended to use it always. 17 | */ 18 | library SafeMath { 19 | /** 20 | * @dev Returns the addition of two unsigned integers, reverting on overflow. 21 | * 22 | * Counterpart to Solidity's `+` operator. 23 | * 24 | * Requirements: 25 | * - Addition cannot overflow. 26 | */ 27 | function add(uint a, uint b) internal pure returns (uint) { 28 | uint c = a + b; 29 | require(c >= a, "add: +"); 30 | 31 | return c; 32 | } 33 | 34 | /** 35 | * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow. 36 | * 37 | * Counterpart to Solidity's `+` operator. 38 | * 39 | * Requirements: 40 | * - Addition cannot overflow. 41 | */ 42 | function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 43 | uint c = a + b; 44 | require(c >= a, errorMessage); 45 | 46 | return c; 47 | } 48 | 49 | /** 50 | * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). 51 | * 52 | * Counterpart to Solidity's `-` operator. 53 | * 54 | * Requirements: 55 | * - Subtraction cannot underflow. 56 | */ 57 | function sub(uint a, uint b) internal pure returns (uint) { 58 | return sub(a, b, "sub: -"); 59 | } 60 | 61 | /** 62 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). 63 | * 64 | * Counterpart to Solidity's `-` operator. 65 | * 66 | * Requirements: 67 | * - Subtraction cannot underflow. 68 | */ 69 | function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 70 | require(b <= a, errorMessage); 71 | uint c = a - b; 72 | 73 | return c; 74 | } 75 | 76 | /** 77 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 78 | * 79 | * Counterpart to Solidity's `*` operator. 80 | * 81 | * Requirements: 82 | * - Multiplication cannot overflow. 83 | */ 84 | function mul(uint a, uint b) internal pure returns (uint) { 85 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 86 | // benefit is lost if 'b' is also tested. 87 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 88 | if (a == 0) { 89 | return 0; 90 | } 91 | 92 | uint c = a * b; 93 | require(c / a == b, "mul: *"); 94 | 95 | return c; 96 | } 97 | 98 | /** 99 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 100 | * 101 | * Counterpart to Solidity's `*` operator. 102 | * 103 | * Requirements: 104 | * - Multiplication cannot overflow. 105 | */ 106 | function mul(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 107 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 108 | // benefit is lost if 'b' is also tested. 109 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 110 | if (a == 0) { 111 | return 0; 112 | } 113 | 114 | uint c = a * b; 115 | require(c / a == b, errorMessage); 116 | 117 | return c; 118 | } 119 | 120 | /** 121 | * @dev Returns the integer division of two unsigned integers. 122 | * Reverts on division by zero. The result is rounded towards zero. 123 | * 124 | * Counterpart to Solidity's `/` operator. Note: this function uses a 125 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 126 | * uses an invalid opcode to revert (consuming all remaining gas). 127 | * 128 | * Requirements: 129 | * - The divisor cannot be zero. 130 | */ 131 | function div(uint a, uint b) internal pure returns (uint) { 132 | return div(a, b, "div: /"); 133 | } 134 | 135 | /** 136 | * @dev Returns the integer division of two unsigned integers. 137 | * Reverts with custom message on division by zero. The result is rounded towards zero. 138 | * 139 | * Counterpart to Solidity's `/` operator. Note: this function uses a 140 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 141 | * uses an invalid opcode to revert (consuming all remaining gas). 142 | * 143 | * Requirements: 144 | * - The divisor cannot be zero. 145 | */ 146 | function div(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 147 | // Solidity only automatically asserts when dividing by 0 148 | require(b > 0, errorMessage); 149 | uint c = a / b; 150 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 151 | 152 | return c; 153 | } 154 | 155 | /** 156 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 157 | * Reverts when dividing by zero. 158 | * 159 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 160 | * opcode (which leaves remaining gas untouched) while Solidity uses an 161 | * invalid opcode to revert (consuming all remaining gas). 162 | * 163 | * Requirements: 164 | * - The divisor cannot be zero. 165 | */ 166 | function mod(uint a, uint b) internal pure returns (uint) { 167 | return mod(a, b, "mod: %"); 168 | } 169 | 170 | /** 171 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 172 | * Reverts with custom message when dividing by zero. 173 | * 174 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 175 | * opcode (which leaves remaining gas untouched) while Solidity uses an 176 | * invalid opcode to revert (consuming all remaining gas). 177 | * 178 | * Requirements: 179 | * - The divisor cannot be zero. 180 | */ 181 | function mod(uint a, uint b, string memory errorMessage) internal pure returns (uint) { 182 | require(b != 0, errorMessage); 183 | return a % b; 184 | } 185 | } 186 | 187 | /** 188 | * @dev Interface of the ERC20 standard as defined in the EIP. 189 | */ 190 | interface IERC20 { 191 | /** 192 | * @dev Returns the amount of tokens owned by `account`. 193 | */ 194 | function balanceOf(address account) external view returns (uint); 195 | } 196 | 197 | interface IKeep3rV1 { 198 | function isKeeper(address) external returns (bool); 199 | function worked(address keeper) external; 200 | } 201 | 202 | interface IYERC20 { 203 | function rebalance() external; 204 | function balanceOf(address account) external view returns (uint); 205 | function token() external view returns (address); 206 | function calcPoolValueInToken() external view returns (uint); 207 | } 208 | 209 | contract YearnV1EarnKeep3r { 210 | using SafeMath for uint; 211 | 212 | uint constant public THRESHOLD = 500; 213 | uint constant public BASE = 10000; 214 | 215 | IYERC20[] internal _tokens; 216 | 217 | constructor() public { 218 | _tokens.push(IYERC20(0xd6aD7a6750A7593E092a9B218d66C0A814a3436e)); 219 | _tokens.push(IYERC20(0x83f798e925BcD4017Eb265844FDDAbb448f1707D)); 220 | _tokens.push(IYERC20(0x73a052500105205d34Daf004eAb301916DA8190f)); 221 | _tokens.push(IYERC20(0xC2cB1040220768554cf699b0d863A3cd4324ce32)); 222 | _tokens.push(IYERC20(0x26EA744E5B887E5205727f55dFBE8685e3b21951)); 223 | _tokens.push(IYERC20(0xE6354ed5bC4b393a5Aad09f21c46E101e692d447)); 224 | _tokens.push(IYERC20(0x04bC0Ab673d88aE9dbC9DA2380cB6B79C4BCa9aE)); 225 | } 226 | 227 | function tokens() external view returns (IYERC20[] memory) { 228 | return _tokens; 229 | } 230 | 231 | modifier upkeep() { 232 | require(KP3R.isKeeper(msg.sender), "::isKeeper: keeper is not registered"); 233 | _; 234 | KP3R.worked(msg.sender); 235 | } 236 | 237 | IKeep3rV1 public constant KP3R = IKeep3rV1(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 238 | 239 | function workable() public view returns (bool) { 240 | for (uint i = 0; i < _tokens.length; i++) { 241 | if (shouldRebalance(_tokens[i])) { 242 | return true; 243 | } 244 | } 245 | return false; 246 | } 247 | 248 | function shouldRebalance(IYERC20 _token) public view returns (bool) { 249 | uint _total = _token.calcPoolValueInToken(); 250 | uint _available = IERC20(_token.token()).balanceOf(address(_token)); 251 | return _available >= _total.mul(THRESHOLD).div(BASE); 252 | } 253 | 254 | function work() external upkeep { 255 | require(workable(), "YearnV1EarnKeep3r::work: !workable()"); 256 | for (uint i = 0; i < _tokens.length; i++) { 257 | if (shouldRebalance(_tokens[i])) { 258 | _tokens[i].rebalance(); 259 | } 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /contracts/utils/cdf.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./script.sol"; 5 | 6 | contract REPL is script { 7 | 8 | uint private constant FIXED_1 = 0x080000000000000000000000000000000; 9 | uint private constant FIXED_2 = 0x100000000000000000000000000000000; 10 | uint private constant SQRT_1 = 13043817825332782212; 11 | uint private constant LOG_E_2 = 6931471806; 12 | uint private constant LOG_10_2 = 3010299957; 13 | uint private constant BASE = 1e10; 14 | 15 | function floorLog2(uint256 _n) internal pure returns (uint8) { 16 | uint8 res = 0; 17 | 18 | if (_n < 256) { 19 | // At most 8 iterations 20 | while (_n > 1) { 21 | _n >>= 1; 22 | res += 1; 23 | } 24 | } else { 25 | // Exactly 8 iterations 26 | for (uint8 s = 128; s > 0; s >>= 1) { 27 | if (_n >= (uint(1) << s)) { 28 | _n >>= s; 29 | res |= s; 30 | } 31 | } 32 | } 33 | 34 | return res; 35 | } 36 | 37 | function generalLog(uint256 x) internal pure returns (uint) { 38 | uint res = 0; 39 | 40 | // If x >= 2, then we compute the integer part of log2(x), which is larger than 0. 41 | if (x >= FIXED_2) { 42 | uint8 count = floorLog2(x / FIXED_1); 43 | x >>= count; // now x < 2 44 | res = count * FIXED_1; 45 | } 46 | 47 | // If x > 1, then we compute the fraction part of log2(x), which is larger than 0. 48 | if (x > FIXED_1) { 49 | for (uint8 i = 127; i > 0; --i) { 50 | x = (x * x) / FIXED_1; // now 1 < x < 4 51 | if (x >= FIXED_2) { 52 | x >>= 1; // now 1 < x < 2 53 | res += uint(1) << (i - 1); 54 | } 55 | } 56 | } 57 | 58 | return res; 59 | } 60 | 61 | function sqrt(uint x) internal pure returns (uint y) { 62 | uint z = (x + 1) / 2; 63 | y = x; 64 | while (z < y) { 65 | y = z; 66 | z = (x / z + z) / 2; 67 | } 68 | } 69 | 70 | function stddev(uint[] memory numbers) public pure returns (uint sd, uint mean) { 71 | uint sum = 0; 72 | for(uint i = 0; i < numbers.length; i++) { 73 | sum += numbers[i]; 74 | } 75 | mean = sum / numbers.length; // Integral value; float not supported in Solidity 76 | sum = 0; 77 | uint i; 78 | for(i = 0; i < numbers.length; i++) { 79 | sum += (numbers[i] - mean) ** 2; 80 | } 81 | sd = sqrt(sum / (numbers.length - 1)); //Integral value; float not supported in Solidity 82 | return (sd, mean); 83 | } 84 | 85 | /** 86 | * @dev computes e ^ (x / FIXED_1) * FIXED_1 87 | * input range: 0 <= x <= OPT_EXP_MAX_VAL - 1 88 | * auto-generated via 'PrintFunctionOptimalExp.py' 89 | * Detailed description: 90 | * - Rewrite the input as a sum of binary exponents and a single residual r, as small as possible 91 | * - The exponentiation of each binary exponent is given (pre-calculated) 92 | * - The exponentiation of r is calculated via Taylor series for e^x, where x = r 93 | * - The exponentiation of the input is calculated by multiplying the intermediate results above 94 | * - For example: e^5.521692859 = e^(4 + 1 + 0.5 + 0.021692859) = e^4 * e^1 * e^0.5 * e^0.021692859 95 | */ 96 | function optimalExp(uint256 x) internal pure returns (uint256) { 97 | uint256 res = 0; 98 | 99 | uint256 y; 100 | uint256 z; 101 | 102 | z = y = x % 0x10000000000000000000000000000000; // get the input modulo 2^(-3) 103 | z = (z * y) / FIXED_1; 104 | res += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!) 105 | z = (z * y) / FIXED_1; 106 | res += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!) 107 | z = (z * y) / FIXED_1; 108 | res += z * 0x0168244fdac78000; // add y^04 * (20! / 04!) 109 | z = (z * y) / FIXED_1; 110 | res += z * 0x004807432bc18000; // add y^05 * (20! / 05!) 111 | z = (z * y) / FIXED_1; 112 | res += z * 0x000c0135dca04000; // add y^06 * (20! / 06!) 113 | z = (z * y) / FIXED_1; 114 | res += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!) 115 | z = (z * y) / FIXED_1; 116 | res += z * 0x000036e0f639b800; // add y^08 * (20! / 08!) 117 | z = (z * y) / FIXED_1; 118 | res += z * 0x00000618fee9f800; // add y^09 * (20! / 09!) 119 | z = (z * y) / FIXED_1; 120 | res += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!) 121 | z = (z * y) / FIXED_1; 122 | res += z * 0x0000000e30dce400; // add y^11 * (20! / 11!) 123 | z = (z * y) / FIXED_1; 124 | res += z * 0x000000012ebd1300; // add y^12 * (20! / 12!) 125 | z = (z * y) / FIXED_1; 126 | res += z * 0x0000000017499f00; // add y^13 * (20! / 13!) 127 | z = (z * y) / FIXED_1; 128 | res += z * 0x0000000001a9d480; // add y^14 * (20! / 14!) 129 | z = (z * y) / FIXED_1; 130 | res += z * 0x00000000001c6380; // add y^15 * (20! / 15!) 131 | z = (z * y) / FIXED_1; 132 | res += z * 0x000000000001c638; // add y^16 * (20! / 16!) 133 | z = (z * y) / FIXED_1; 134 | res += z * 0x0000000000001ab8; // add y^17 * (20! / 17!) 135 | z = (z * y) / FIXED_1; 136 | res += z * 0x000000000000017c; // add y^18 * (20! / 18!) 137 | z = (z * y) / FIXED_1; 138 | res += z * 0x0000000000000014; // add y^19 * (20! / 19!) 139 | z = (z * y) / FIXED_1; 140 | res += z * 0x0000000000000001; // add y^20 * (20! / 20!) 141 | res = res / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0! 142 | 143 | if ((x & 0x010000000000000000000000000000000) != 0) 144 | res = (res * 0x1c3d6a24ed82218787d624d3e5eba95f9) / 0x18ebef9eac820ae8682b9793ac6d1e776; // multiply by e^2^(-3) 145 | if ((x & 0x020000000000000000000000000000000) != 0) 146 | res = (res * 0x18ebef9eac820ae8682b9793ac6d1e778) / 0x1368b2fc6f9609fe7aceb46aa619baed4; // multiply by e^2^(-2) 147 | if ((x & 0x040000000000000000000000000000000) != 0) 148 | res = (res * 0x1368b2fc6f9609fe7aceb46aa619baed5) / 0x0bc5ab1b16779be3575bd8f0520a9f21f; // multiply by e^2^(-1) 149 | if ((x & 0x080000000000000000000000000000000) != 0) 150 | res = (res * 0x0bc5ab1b16779be3575bd8f0520a9f21e) / 0x0454aaa8efe072e7f6ddbab84b40a55c9; // multiply by e^2^(+0) 151 | if ((x & 0x100000000000000000000000000000000) != 0) 152 | res = (res * 0x0454aaa8efe072e7f6ddbab84b40a55c5) / 0x00960aadc109e7a3bf4578099615711ea; // multiply by e^2^(+1) 153 | if ((x & 0x200000000000000000000000000000000) != 0) 154 | res = (res * 0x00960aadc109e7a3bf4578099615711d7) / 0x0002bf84208204f5977f9a8cf01fdce3d; // multiply by e^2^(+2) 155 | if ((x & 0x400000000000000000000000000000000) != 0) 156 | res = (res * 0x0002bf84208204f5977f9a8cf01fdc307) / 0x0000003c6ab775dd0b95b4cbee7e65d11; // multiply by e^2^(+3) 157 | 158 | return res; 159 | } 160 | 161 | function ncdf(uint x) internal pure returns (uint) { 162 | int t1 = int(1e7 + (2315419 * x / FIXED_1)); 163 | uint exp = x / 2 * x / FIXED_1; 164 | int d = int(3989423 * FIXED_1 / optimalExp(uint(exp))); 165 | uint prob = uint(d * (3193815 + ( -3565638 + (17814780 + (-18212560 + 13302740 * 1e7 / t1) * 1e7 / t1) * 1e7 / t1) * 1e7 / t1) * 1e7 / t1); 166 | if( x > 0 ) prob = 1e14 - prob; 167 | return prob; 168 | } 169 | 170 | function vol(uint[] memory p) internal pure returns (uint x) { 171 | for (uint8 i = 1; i <= (p.length-1); i++) { 172 | x += ((generalLog(p[i] * FIXED_1) - generalLog(p[i-1] * FIXED_1)))**2; 173 | } 174 | x = sqrt(uint(252) * sqrt(x / (p.length-1))); 175 | return uint(1e18) * x / SQRT_1; 176 | } 177 | 178 | function run() public { 179 | run(this.repl).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 180 | } 181 | 182 | function repl() external { 183 | uint sp = 16199; 184 | uint st = 16000; 185 | 186 | uint d1 = generalLog(FIXED_1 * sp / st) * LOG_E_2 / BASE; // ln(SP/ST) 187 | uint cdf = ncdf(d1); 188 | 189 | 190 | 191 | fmt.printf("d1=%u\n",abi.encode(d1)); 192 | fmt.printf("cdf=%d\n",abi.encode(cdf)); 193 | fmt.printf("FIXED_1=%u\n",abi.encode(FIXED_1)); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /contracts/utils/logn.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./script.sol"; 5 | 6 | contract REPL is script { 7 | 8 | uint private constant FIXED_1 = 0x080000000000000000000000000000000; 9 | uint private constant FIXED_2 = 0x100000000000000000000000000000000; 10 | uint private constant SQRT_1 = 13043817825332782212; 11 | uint private constant LOG_10_2 = 3010299957; 12 | uint private constant BASE = 1e10; 13 | 14 | function floorLog2(uint256 _n) internal pure returns (uint8) { 15 | uint8 res = 0; 16 | 17 | if (_n < 256) { 18 | // At most 8 iterations 19 | while (_n > 1) { 20 | _n >>= 1; 21 | res += 1; 22 | } 23 | } else { 24 | // Exactly 8 iterations 25 | for (uint8 s = 128; s > 0; s >>= 1) { 26 | if (_n >= (uint(1) << s)) { 27 | _n >>= s; 28 | res |= s; 29 | } 30 | } 31 | } 32 | 33 | return res; 34 | } 35 | 36 | function generalLog(uint256 x) internal pure returns (uint) { 37 | uint res = 0; 38 | 39 | // If x >= 2, then we compute the integer part of log2(x), which is larger than 0. 40 | if (x >= FIXED_2) { 41 | uint8 count = floorLog2(x / FIXED_1); 42 | x >>= count; // now x < 2 43 | res = count * FIXED_1; 44 | } 45 | 46 | // If x > 1, then we compute the fraction part of log2(x), which is larger than 0. 47 | if (x > FIXED_1) { 48 | for (uint8 i = 127; i > 0; --i) { 49 | x = (x * x) / FIXED_1; // now 1 < x < 4 50 | if (x >= FIXED_2) { 51 | x >>= 1; // now 1 < x < 2 52 | res += uint(1) << (i - 1); 53 | } 54 | } 55 | } 56 | 57 | return res * LOG_10_2 / BASE; 58 | } 59 | 60 | function sqrt(uint x) internal pure returns (uint y) { 61 | uint z = (x + 1) / 2; 62 | y = x; 63 | while (z < y) { 64 | y = z; 65 | z = (x / z + z) / 2; 66 | } 67 | } 68 | 69 | function vol(uint[] memory p) internal pure returns (uint x) { 70 | for (uint8 i = 1; i <= (p.length-1); i++) { 71 | x += ((generalLog(p[i] * FIXED_1) - generalLog(p[i-1] * FIXED_1)))**2; 72 | //denom += FIXED_1**2; 73 | } 74 | //return (sum, denom); 75 | x = sqrt(uint(252) * sqrt(x / (p.length-2))); 76 | return uint(1e18) * x / SQRT_1; 77 | } 78 | 79 | function run() public { 80 | run(this.repl).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 81 | } 82 | 83 | function repl() external { 84 | 85 | uint[] memory points = new uint[](12); 86 | points[0] = 3365048423556510000; 87 | points[1] = 3366516801778030000; 88 | points[2] = 3373817387435220000; 89 | points[3] = 3379320958608430000; 90 | points[4] = 3382987161136880000; 91 | points[5] = 3405998172003360000; 92 | points[6] = 3406484665993710000; 93 | points[7] = 3408043106034020000; 94 | points[8] = 3408287499677610000; 95 | points[9] = 3409044636451580000; 96 | points[10] = 3412400414311440000; 97 | points[11] = 3413808337176390000; 98 | /* 99 | 100 | (uint res1) = generalLog(3365048423556510000 * FIXED_1); 101 | (uint res2) = generalLog(3366516801778030000 * FIXED_1); 102 | 103 | res1 = res1 * LOG_10_2 / BASE; 104 | res2 = res2 * LOG_10_2 / BASE; 105 | 106 | uint rt = uint((res2 - res1)**2); 107 | 108 | uint sqrtRT = sqrt(rt); 109 | uint retVol = uint(252) * sqrtRT; 110 | uint sqrtRV = sqrt(retVol); 111 | uint sqrtFIXED_1 = sqrt(FIXED_1); 112 | 113 | 114 | uint vol = uint(1e18) * sqrtRV / SQRT_1; 115 | */ 116 | uint x = vol(points); 117 | fmt.printf("x=%u\n",abi.encode(x)); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /docs/ERC20.md: -------------------------------------------------------------------------------- 1 | ## `ERC20` 2 | 3 | 4 | 5 | 6 | 7 | 8 | ### `transfer(address to, uint256 value) → bool` (external) 9 | 10 | 11 | 12 | 13 | 14 | ### `transferFrom(address from, address to, uint256 value) → bool` (external) 15 | 16 | 17 | 18 | 19 | 20 | ### `balanceOf(address account) → uint256` (external) 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/Factory.md: -------------------------------------------------------------------------------- 1 | ## `Factory` 2 | 3 | 4 | 5 | 6 | 7 | 8 | ### `getPair(address tokenA, address tokenB) → address pair` (external) 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/Governance.md: -------------------------------------------------------------------------------- 1 | # Governance 2 | 3 | KPR governance by design has a low overhead, it is not meant to be protocol intensive. The focus is simply on reviewing and approving jobs, and if absolutely required mitigate disputes or blacklist keepers. 4 | 5 | ## Participants 6 | 7 | Only bonded Keepers may participate in governance. To participate in governance a keeper must first ```bond``` KPR, once bonded they can ```delegate``` voting rights to either themselves or another party. 8 | 9 | ## Managing Jobs 10 | 11 | The core function of governance is to approve and include jobs, when liquidity is provided for a job, a proposal is automatically created to include them for review. Bonded keepers will review the contracts and decide if they should be included. It is important that jobs code defensively, assume keepers will only include your job to maximize their returns, as such maximum payment thresholds have been implemented. 12 | 13 | ``` 14 | /** 15 | * @notice Allows governance to add new job systems 16 | * @param job address of the contract for which work should be performed 17 | */ 18 | function addJob(address job) external 19 | ``` 20 | 21 | ``` 22 | /** 23 | * @notice Allows governance to remove a job from the systems 24 | * @param job address of the contract for which work should be performed 25 | */ 26 | function removeJob(address job) external 27 | ``` 28 | 29 | ## Managing Keepers 30 | 31 | As a last resort, governance has certain rights over managing Keepers, these include lodging disputes, slashing, revoking access, and resolving disputes. 32 | 33 | ### Dispute Management 34 | 35 | ``` 36 | /** 37 | * @notice allows governance to create a dispute for a given keeper 38 | * @param keeper the address in dispute 39 | */ 40 | function dispute(address keeper) external 41 | ``` 42 | 43 | ``` 44 | /** 45 | * @notice allows governance to resolve a dispute on a keeper 46 | * @param keeper the address cleared 47 | */ 48 | function resolve(address keeper) external 49 | ``` 50 | 51 | ### Slashing 52 | 53 | ``` 54 | /** 55 | * @notice allows governance to slash a keeper based on a dispute 56 | * @param bonded the asset being slashed 57 | * @param keeper the address being slashed 58 | * @param amount the amount being slashed 59 | */ 60 | function slash(address bonded, address keeper, uint amount) public 61 | ``` 62 | 63 | ### Blacklist 64 | 65 | ``` 66 | /** 67 | * @notice blacklists a keeper from participating in the network 68 | * @param keeper the address being slashed 69 | */ 70 | function revoke(address keeper) external 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/JOBS.md: -------------------------------------------------------------------------------- 1 | # Managing Jobs 2 | 3 | ## Quick Start Examples 4 | 5 | ### Simple Keeper 6 | 7 | To setup a keeper function simply add the following modifier; 8 | 9 | ``` 10 | modifier keep() { 11 | require(KPR.isKeeper(msg.sender), "::isKeeper: keeper is not registered"); 12 | _; 13 | KPR.worked(msg.sender); 14 | } 15 | ``` 16 | 17 | The above will make sure the caller is a registered keeper as well as reward them with an amount of KPR equal to their gas spent + premium. Make sure to have credit assigned in the Keep3r system for the relevant job. 18 | 19 | ## Adding Jobs 20 | 21 | Jobs can be created directly via governance or by submitting a job proposal to governance automatically via adding liquidity. 22 | 23 | ### Submit a job via governance 24 | 25 | Simply create a new proposal via governance to add a new job 26 | 27 | ``` 28 | /** 29 | * @notice Allows governance to add new job systems 30 | * @param job address of the contract for which work should be performed 31 | */ 32 | function addJob(address job) external; 33 | ``` 34 | 35 | ### Submit a job via adding liquidity 36 | 37 | You will need to provide liquidity to one of the approved liquidity pairs (for example KPR-ETH). You put your LP tokens in escrow and receive credit. When the credit is used up, you can simply withdraw the LP tokens. You will receive 100% of the LP tokens back that you deposited. 38 | 39 | ``` 40 | /** 41 | * @notice Allows liquidity providers to submit jobs 42 | * @param liquidity the liquidity being added 43 | * @param job the job to assign credit to 44 | * @param amount the amount of liquidity tokens to use 45 | */ 46 | function addLiquidityToJob(address liquidity, address job, uint amount) external 47 | ``` 48 | 49 | ## Managing Credit 50 | 51 | Jobs need credit to be able to pay keepers, this credit can either be paid for directly, or by being a liquidity provider in the system. If you pay directly, this is a direct expense, if you are a liquidity provider, you get all your liquidity back after you are done being a provider. 52 | 53 | ### Add credit to a job via Liquidity 54 | 55 | Step 1 is to provide LP tokens as credit. You receive all your LP tokens back when you no longer need to provide credit for a contract. 56 | 57 | ``` 58 | /** 59 | * @notice Allows liquidity providers to submit jobs 60 | * @param liquidity the liquidity being added 61 | * @param job the job to assign credit to 62 | * @param amount the amount of liquidity tokens to use 63 | */ 64 | function addLiquidityToJob(address liquidity, address job, uint amount) external 65 | ``` 66 | 67 | Wait ```LIQUIDITYBOND``` (default 3 days) days. 68 | 69 | ``` 70 | /** 71 | * @notice Applies the credit provided in addLiquidityToJob to the job 72 | * @param provider the liquidity provider 73 | * @param liquidity the pair being added as liquidity 74 | * @param job the job that is receiving the credit 75 | */ 76 | function applyCreditToJob(address provider, address liquidity, address job) external 77 | ``` 78 | 79 | ### Remove liquidity from a job 80 | 81 | ``` 82 | /** 83 | * @notice Unbond liquidity for a job 84 | * @param liquidity the pair being unbound 85 | * @param job the job being unbound from 86 | * @param amount the amount of liquidity being removed 87 | */ 88 | function unbondLiquidityFromJob(address liquidity, address job, uint amount) external 89 | ``` 90 | 91 | Wait ```UNBOND``` (default 14 days) days. 92 | 93 | ``` 94 | /** 95 | * @notice Allows liquidity providers to remove liquidity 96 | * @param liquidity the pair being unbound 97 | * @param job the job being unbound from 98 | */ 99 | function removeLiquidityFromJob(address liquidity, address job) external 100 | ``` 101 | 102 | ### Adding credit directly (non ETH) 103 | 104 | ``` 105 | /** 106 | * @notice Add credit to a job to be paid out for work 107 | * @param credit the credit being assigned to the job 108 | * @param job the job being credited 109 | * @param amount the amount of credit being added to the job 110 | */ 111 | function addCredit(address credit, address job, uint amount) external 112 | ``` 113 | 114 | ### Adding credit directly (ETH) 115 | 116 | ``` 117 | /** 118 | * @notice Add ETH credit to a job to be paid out for work 119 | * @param job the job being credited 120 | */ 121 | function addCreditETH(address job) external payable 122 | ``` 123 | 124 | ## Selecting Keepers 125 | 126 | Dependent on your requirements you might allow any keepers, or you want to limit specific keepers, you can filter keepers based on ```age```, ```bond```, ```total earned funds```, or even arbitrary values such as additional bonded tokens. 127 | 128 | ### No access control 129 | 130 | Accept all keepers in the system. 131 | 132 | ``` 133 | /** 134 | * @notice confirms if the current keeper is registered, can be used for general (non critical) functions 135 | * @param keeper the keeper being investigated 136 | * @return true/false if the address is a keeper 137 | */ 138 | function isKeeper(address keeper) external returns (bool) 139 | ``` 140 | 141 | ### Filtered access control 142 | 143 | Filter keepers based on bonded amount, earned funds, and age in system. 144 | 145 | ``` 146 | /** 147 | * @notice confirms if the current keeper is registered and has a minimum bond, should be used for protected functions 148 | * @param keeper the keeper being investigated 149 | * @param minBond the minimum requirement for the asset provided in bond 150 | * @param earned the total funds earned in the keepers lifetime 151 | * @param age the age of the keeper in the system 152 | * @return true/false if the address is a keeper and has more than the bond 153 | */ 154 | function isMinKeeper(address keeper, uint minBond, uint earned, uint age) external returns (bool) 155 | ``` 156 | 157 | Additionally you can filter keepers on additional bonds, for example a keeper might need to have ```SNX``` to be able to participate in the [Synthetix](https://synthetix.io/) ecosystem. 158 | 159 | ``` 160 | /** 161 | * @notice confirms if the current keeper is registered and has a minimum bond, should be used for protected functions 162 | * @param keeper the keeper being investigated 163 | * @param bond the bound asset being evaluated 164 | * @param minBond the minimum requirement for the asset provided in bond 165 | * @param earned the total funds earned in the keepers lifetime 166 | * @param age the age of the keeper in the system 167 | * @return true/false if the address is a keeper and has more than the bond 168 | */ 169 | function isBondedKeeper(address keeper, address bond, uint minBond, uint earned, uint age) external returns (bool) 170 | ``` 171 | 172 | ## Paying Keepers 173 | 174 | There are three primary payment mechanisms and these are based on the credit provided; 175 | 176 | * Pay via liquidity provided tokens (based on ```addLiquidityToJob```) 177 | * Pay in direct ETH (based on ```addCreditETH```) 178 | * Pay in direct token (based on ```addCredit```) 179 | 180 | ## Auto Pay 181 | 182 | If you don't want to worry about calculating payment, you can simply let the system calculate the payment itself; 183 | 184 | ``` 185 | /** 186 | * @notice Implemented by jobs to show that a keeper performed work 187 | * @param keeper address of the keeper that performed the work 188 | */ 189 | function worked(address keeper) external 190 | ``` 191 | 192 | ### Pay with KPR 193 | 194 | The maximum amount that can be paid out per call is ```(gasUsed * fastGasPrice) * 1.1``` 195 | 196 | ``` 197 | /** 198 | * @notice Implemented by jobs to show that a keeper performed work 199 | * @param keeper address of the keeper that performed the work 200 | * @param amount the reward that should be allocated 201 | */ 202 | function workReceipt(address keeper, uint amount) external 203 | ``` 204 | 205 | ### Pay with token 206 | 207 | There is no limit on how many tokens can be paid out via this mechanism 208 | 209 | ``` 210 | /** 211 | * @notice Implemented by jobs to show that a keeper performed work 212 | * @param credit the asset being awarded to the keeper 213 | * @param keeper address of the keeper that performed the work 214 | * @param amount the reward that should be allocated 215 | */ 216 | function receipt(address credit, address keeper, uint amount) external 217 | ``` 218 | 219 | ### Pay with ETH 220 | 221 | There is no limit on how many tokens can be paid out via this mechanism 222 | 223 | ``` 224 | /** 225 | * @notice Implemented by jobs to show that a keeper performend work 226 | * @param keeper address of the keeper that performed the work 227 | * @param amount the amount of ETH sent to the keeper 228 | */ 229 | function receiptETH(address keeper, uint amount) external 230 | ``` 231 | -------------------------------------------------------------------------------- /docs/Keep3r.md: -------------------------------------------------------------------------------- 1 | ## `Keep3r` 2 | 3 | 4 | 5 | 6 | 7 | 8 | ### `delegate(address delegatee)` (public) 9 | 10 | Delegate votes from `msg.sender` to `delegatee` 11 | 12 | 13 | 14 | 15 | ### `delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s)` (public) 16 | 17 | Delegates votes from signatory to `delegatee` 18 | 19 | 20 | 21 | 22 | ### `getCurrentVotes(address account) → uint256` (external) 23 | 24 | Gets the current votes balance for `account` 25 | 26 | 27 | 28 | 29 | ### `getPriorVotes(address account, uint256 blockNumber) → uint256` (public) 30 | 31 | Determine the prior number of votes for an account as of a block number 32 | 33 | 34 | Block number must be a finalized block or else this function will revert to prevent misinformation. 35 | 36 | 37 | ### `_delegate(address delegator, address delegatee)` (internal) 38 | 39 | 40 | 41 | 42 | 43 | ### `_moveDelegates(address srcRep, address dstRep, uint256 amount)` (internal) 44 | 45 | 46 | 47 | 48 | 49 | ### `_writeCheckpoint(address delegatee, uint32 nCheckpoints, uint256 oldVotes, uint256 newVotes)` (internal) 50 | 51 | 52 | 53 | 54 | 55 | ### `safe32(uint256 n, string errorMessage) → uint32` (internal) 56 | 57 | 58 | 59 | 60 | 61 | ### `addCreditETH(address job)` (external) 62 | 63 | Add ETH credit to a job to be paid out for work 64 | 65 | 66 | 67 | 68 | ### `addCredit(address credit, address job, uint256 amount)` (external) 69 | 70 | Add credit to a job to be paid out for work 71 | 72 | 73 | 74 | 75 | ### `approveLiquidity(address liquidity)` (external) 76 | 77 | Approve a liquidity pair for being accepted in future 78 | 79 | 80 | 81 | 82 | ### `revokeLiquidity(address liquidity)` (external) 83 | 84 | Revoke a liquidity pair from being accepted in future 85 | 86 | 87 | 88 | 89 | ### `pairs() → address[]` (external) 90 | 91 | Displays all accepted liquidity pairs 92 | 93 | 94 | 95 | ### `addLiquidityToJob(address liquidity, address job, uint256 amount)` (external) 96 | 97 | Allows liquidity providers to submit jobs 98 | 99 | 100 | 101 | 102 | ### `applyCreditToJob(address provider, address liquidity, address job)` (external) 103 | 104 | Applies the credit provided in addLiquidityToJob to the job 105 | 106 | 107 | 108 | 109 | ### `unbondLiquidityFromJob(address liquidity, address job, uint256 amount)` (external) 110 | 111 | Unbond liquidity for a pending keeper job 112 | 113 | 114 | 115 | 116 | ### `removeLiquidityFromJob(address liquidity, address job)` (external) 117 | 118 | Allows liquidity providers to remove liquidity 119 | 120 | 121 | 122 | 123 | ### `mint(uint256 amount)` (external) 124 | 125 | Allows governance to mint new tokens to treasury 126 | 127 | 128 | 129 | 130 | ### `burn(uint256 amount)` (external) 131 | 132 | burn owned tokens 133 | 134 | 135 | 136 | 137 | ### `_mint(address dst, uint256 amount)` (internal) 138 | 139 | 140 | 141 | 142 | 143 | ### `_burn(address dst, uint256 amount)` (internal) 144 | 145 | 146 | 147 | 148 | 149 | ### `workReceipt(address keeper, uint256 amount)` (external) 150 | 151 | Implemented by jobs to show that a keeper performend work 152 | 153 | 154 | 155 | 156 | ### `receipt(address credit, address keeper, uint256 amount)` (external) 157 | 158 | Implemented by jobs to show that a keeper performend work 159 | 160 | 161 | 162 | 163 | ### `receiptETH(address keeper, uint256 amount)` (external) 164 | 165 | Implemented by jobs to show that a keeper performend work 166 | 167 | 168 | 169 | 170 | ### `_bond(address bonding, address _from, uint256 _amount)` (internal) 171 | 172 | 173 | 174 | 175 | 176 | ### `_unbond(address bonding, address _from, uint256 _amount)` (internal) 177 | 178 | 179 | 180 | 181 | 182 | ### `addJob(address job)` (external) 183 | 184 | Allows governance to add new job systems 185 | 186 | 187 | 188 | 189 | ### `getJobs() → address[]` (external) 190 | 191 | Full listing of all jobs ever added 192 | 193 | 194 | 195 | 196 | ### `removeJob(address job)` (external) 197 | 198 | Allows governance to remove a job from the systems 199 | 200 | 201 | 202 | 203 | ### `setKeep3rHelper(contract Keep3rHelper _kprh)` (external) 204 | 205 | Allows governance to change the Keep3rHelper for max spend 206 | 207 | 208 | 209 | 210 | ### `setGovernance(address _governance)` (external) 211 | 212 | Allows governance to change governance (for future upgradability) 213 | 214 | 215 | 216 | 217 | ### `acceptGovernance()` (external) 218 | 219 | Allows pendingGovernance to accept their role as governance (protection pattern) 220 | 221 | 222 | 223 | ### `isKeeper(address keeper) → bool` (external) 224 | 225 | confirms if the current keeper is registered, can be used for general (non critical) functions 226 | 227 | 228 | 229 | 230 | ### `isMinKeeper(address keeper, uint256 minBond, uint256 earned, uint256 age) → bool` (external) 231 | 232 | confirms if the current keeper is registered and has a minimum bond, should be used for protected functions 233 | 234 | 235 | 236 | 237 | ### `isBondedKeeper(address keeper, address bond, uint256 minBond, uint256 earned, uint256 age) → bool` (external) 238 | 239 | confirms if the current keeper is registered and has a minimum bond, should be used for protected functions 240 | 241 | 242 | 243 | 244 | ### `bond(address bonding, uint256 amount)` (external) 245 | 246 | begin the bonding process for a new keeper 247 | 248 | 249 | 250 | 251 | ### `getKeepers() → address[]` (external) 252 | 253 | get full list of keepers in the system 254 | 255 | 256 | 257 | ### `activate(address bonding)` (external) 258 | 259 | allows a keeper to activate/register themselves after bonding 260 | 261 | 262 | 263 | 264 | ### `unbond(address bonding, uint256 amount)` (external) 265 | 266 | begin the unbonding process to stop being a keeper 267 | 268 | 269 | 270 | 271 | ### `withdraw(address bonding)` (external) 272 | 273 | withdraw funds after unbonding has finished 274 | 275 | 276 | 277 | 278 | ### `down(address keeper)` (external) 279 | 280 | slash a keeper for downtime 281 | 282 | 283 | 284 | 285 | ### `dispute(address keeper) → uint256` (external) 286 | 287 | allows governance to create a dispute for a given keeper 288 | 289 | 290 | 291 | 292 | ### `slash(address bonded, address keeper, uint256 amount)` (public) 293 | 294 | allows governance to slash a keeper based on a dispute 295 | 296 | 297 | 298 | 299 | ### `revoke(address keeper)` (external) 300 | 301 | blacklists a keeper from participating in the network 302 | 303 | 304 | 305 | 306 | ### `resolve(address keeper)` (external) 307 | 308 | allows governance to resolve a dispute on a keeper 309 | 310 | 311 | 312 | 313 | ### `allowance(address account, address spender) → uint256` (external) 314 | 315 | Get the number of tokens `spender` is approved to spend on behalf of `account` 316 | 317 | 318 | 319 | 320 | ### `approve(address spender, uint256 amount) → bool` (public) 321 | 322 | Approve `spender` to transfer up to `amount` from `src` 323 | 324 | 325 | This will overwrite the approval amount for `spender` 326 | and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve) 327 | 328 | 329 | ### `permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)` (external) 330 | 331 | Triggers an approval from owner to spends 332 | 333 | 334 | 335 | 336 | ### `balanceOf(address account) → uint256` (external) 337 | 338 | Get the number of tokens held by the `account` 339 | 340 | 341 | 342 | 343 | ### `transfer(address dst, uint256 amount) → bool` (public) 344 | 345 | Transfer `amount` tokens from `msg.sender` to `dst` 346 | 347 | 348 | 349 | 350 | ### `transferFrom(address src, address dst, uint256 amount) → bool` (external) 351 | 352 | Transfer `amount` tokens from `src` to `dst` 353 | 354 | 355 | 356 | 357 | ### `_transferTokens(address src, address dst, uint256 amount)` (internal) 358 | 359 | 360 | 361 | 362 | 363 | ### `_getChainId() → uint256` (internal) 364 | 365 | 366 | 367 | 368 | 369 | 370 | ### `DelegateChanged(address delegator, address fromDelegate, address toDelegate)` 371 | 372 | An event thats emitted when an account changes its delegate 373 | 374 | 375 | 376 | ### `DelegateVotesChanged(address delegate, uint256 previousBalance, uint256 newBalance)` 377 | 378 | An event thats emitted when a delegate account's vote balance changes 379 | 380 | 381 | 382 | ### `Transfer(address from, address to, uint256 amount)` 383 | 384 | The standard EIP-20 transfer event 385 | 386 | 387 | 388 | ### `Approval(address owner, address spender, uint256 amount)` 389 | 390 | The standard EIP-20 approval event 391 | 392 | 393 | 394 | ### `SubmitJob(address job, address provider, uint256 block, uint256 credit)` 395 | 396 | Submit a job 397 | 398 | 399 | 400 | ### `ApplyCredit(address job, address provider, uint256 block, uint256 credit)` 401 | 402 | Apply credit to a job 403 | 404 | 405 | 406 | ### `RemoveJob(address job, address provider, uint256 block, uint256 credit)` 407 | 408 | Remove credit for a job 409 | 410 | 411 | 412 | ### `UnbondJob(address job, address provider, uint256 block, uint256 credit)` 413 | 414 | Unbond credit for a job 415 | 416 | 417 | 418 | ### `JobAdded(address job, uint256 block, address governance)` 419 | 420 | Added a Job 421 | 422 | 423 | 424 | ### `JobRemoved(address job, uint256 block, address governance)` 425 | 426 | Removed a job 427 | 428 | 429 | 430 | ### `KeeperWorked(address credit, address job, address keeper, uint256 block)` 431 | 432 | Worked a job 433 | 434 | 435 | 436 | ### `KeeperBonding(address keeper, uint256 block, uint256 active, uint256 bond)` 437 | 438 | Keeper bonding 439 | 440 | 441 | 442 | ### `KeeperBonded(address keeper, uint256 block, uint256 activated, uint256 bond)` 443 | 444 | Keeper bonded 445 | 446 | 447 | 448 | ### `KeeperUnbonding(address keeper, uint256 block, uint256 deactive, uint256 bond)` 449 | 450 | Keeper unbonding 451 | 452 | 453 | 454 | ### `KeeperUnbound(address keeper, uint256 block, uint256 deactivated, uint256 bond)` 455 | 456 | Keeper unbound 457 | 458 | 459 | 460 | ### `KeeperSlashed(address keeper, address slasher, uint256 block, uint256 slash)` 461 | 462 | Keeper slashed 463 | 464 | 465 | 466 | ### `KeeperDispute(address keeper, uint256 block)` 467 | 468 | Keeper disputed 469 | 470 | 471 | 472 | ### `KeeperResolved(address keeper, uint256 block)` 473 | 474 | Keeper resolved 475 | 476 | 477 | 478 | ### `AddCredit(address credit, address job, address creditor, uint256 block, uint256 amount)` 479 | 480 | 481 | 482 | 483 | 484 | -------------------------------------------------------------------------------- /docs/Keep3rHelper.md: -------------------------------------------------------------------------------- 1 | ## `Keep3rHelper` 2 | 3 | 4 | 5 | 6 | 7 | 8 | ### `getQuoteLimit(uint256 gasUsed) → uint256` (external) 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/Keepers.md: -------------------------------------------------------------------------------- 1 | # Keepers 2 | 3 | Keepers are bots, scripts, other contracts, or simply EOA accounts that trigger events. This can be submitting a signed TX on behalf of a third party, calling a transaction at a specific time, or more complex functionality. 4 | 5 | Each time you execute such a function, you are rewarded in either ETH, tokens, or the systems native token KPR. The maximum amount of KPR receivable is gasUsed + premium (configured by governance). 6 | 7 | Jobs might require keepers that have a minimum amount of bonded tokens, have earned a minimum amount of fees, or have been in the system longer than a certain period of time. 8 | 9 | At the most simple level, they simply require a keeper to be registered in the system. 10 | 11 | ## Becoming a Keeper 12 | 13 | To become a keeper, you simply need to call ```bond(address,uint)```, no funds are required to become a keeper, however certain jobs might require a minimum amount of funds. 14 | 15 | ``` 16 | /** 17 | * @notice begin the bonding process for a new keeper 18 | * @param bonding the asset being bound 19 | * @param amount the amount of bonding asset being bound 20 | */ 21 | function bond(address bonding, uint amount) external 22 | ``` 23 | 24 | After waiting ```BOND``` days (default 3 days) and you can activate as a keeper; 25 | 26 | ``` 27 | /** 28 | * @notice allows a keeper to activate/register themselves after bonding 29 | * @param bonding the asset being activated as bond collateral 30 | */ 31 | function activate(address bonding) external 32 | ``` 33 | 34 | ## Removing a Keeper 35 | 36 | If you no longer wish to participate you can unbond to deactivate. 37 | 38 | ``` 39 | /** 40 | * @notice begin the unbonding process to stop being a keeper 41 | * @param bonding the asset being unbound 42 | * @param amount allows for partial unbonding 43 | */ 44 | function unbond(address bonding, uint amount) external 45 | ``` 46 | 47 | After waiting ```UNBOND``` days (default 14 days) you can withdraw any bonded assets 48 | 49 | ``` 50 | /** 51 | * @notice withdraw funds after unbonding has finished 52 | * @param bonding the asset to withdraw from the bonding pool 53 | */ 54 | function withdraw(address bonding) external 55 | ``` 56 | 57 | ## Additional Requirements 58 | 59 | Some jobs might have additional requirements such as minimum bonded protocol tokens (for example SNX). In such cases you would need to bond a minimum amount of SNX before you may qualify for the job. 60 | -------------------------------------------------------------------------------- /docs/SafeMath.md: -------------------------------------------------------------------------------- 1 | ## `SafeMath` 2 | 3 | 4 | 5 | Wrappers over Solidity's arithmetic operations with added overflow 6 | checks. 7 | Arithmetic operations in Solidity wrap on overflow. This can easily result 8 | in bugs, because programmers usually assume that an overflow raises an 9 | error, which is the standard behavior in high level programming languages. 10 | `SafeMath` restores this intuition by reverting the transaction when an 11 | operation overflows. 12 | Using this library instead of the unchecked operations eliminates an entire 13 | class of bugs, so it's recommended to use it always. 14 | 15 | 16 | ### `add(uint256 a, uint256 b) → uint256` (internal) 17 | 18 | 19 | 20 | Returns the addition of two unsigned integers, reverting on overflow. 21 | Counterpart to Solidity's `+` operator. 22 | Requirements: 23 | - Addition cannot overflow. 24 | 25 | ### `add(uint256 a, uint256 b, string errorMessage) → uint256` (internal) 26 | 27 | 28 | 29 | Returns the addition of two unsigned integers, reverting with custom message on overflow. 30 | Counterpart to Solidity's `+` operator. 31 | Requirements: 32 | - Addition cannot overflow. 33 | 34 | ### `sub(uint256 a, uint256 b) → uint256` (internal) 35 | 36 | 37 | 38 | Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). 39 | Counterpart to Solidity's `-` operator. 40 | Requirements: 41 | - Subtraction cannot underflow. 42 | 43 | ### `sub(uint256 a, uint256 b, string errorMessage) → uint256` (internal) 44 | 45 | 46 | 47 | Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). 48 | Counterpart to Solidity's `-` operator. 49 | Requirements: 50 | - Subtraction cannot underflow. 51 | 52 | ### `mul(uint256 a, uint256 b) → uint256` (internal) 53 | 54 | 55 | 56 | Returns the multiplication of two unsigned integers, reverting on overflow. 57 | Counterpart to Solidity's `*` operator. 58 | Requirements: 59 | - Multiplication cannot overflow. 60 | 61 | ### `mul(uint256 a, uint256 b, string errorMessage) → uint256` (internal) 62 | 63 | 64 | 65 | Returns the multiplication of two unsigned integers, reverting on overflow. 66 | Counterpart to Solidity's `*` operator. 67 | Requirements: 68 | - Multiplication cannot overflow. 69 | 70 | ### `div(uint256 a, uint256 b) → uint256` (internal) 71 | 72 | 73 | 74 | Returns the integer division of two unsigned integers. 75 | Reverts on division by zero. The result is rounded towards zero. 76 | Counterpart to Solidity's `/` operator. Note: this function uses a 77 | `revert` opcode (which leaves remaining gas untouched) while Solidity 78 | uses an invalid opcode to revert (consuming all remaining gas). 79 | Requirements: 80 | - The divisor cannot be zero. 81 | 82 | ### `div(uint256 a, uint256 b, string errorMessage) → uint256` (internal) 83 | 84 | 85 | 86 | Returns the integer division of two unsigned integers. 87 | Reverts with custom message on division by zero. The result is rounded towards zero. 88 | Counterpart to Solidity's `/` operator. Note: this function uses a 89 | `revert` opcode (which leaves remaining gas untouched) while Solidity 90 | uses an invalid opcode to revert (consuming all remaining gas). 91 | Requirements: 92 | - The divisor cannot be zero. 93 | 94 | ### `mod(uint256 a, uint256 b) → uint256` (internal) 95 | 96 | 97 | 98 | Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 99 | Reverts when dividing by zero. 100 | Counterpart to Solidity's `%` operator. This function uses a `revert` 101 | opcode (which leaves remaining gas untouched) while Solidity uses an 102 | invalid opcode to revert (consuming all remaining gas). 103 | Requirements: 104 | - The divisor cannot be zero. 105 | 106 | ### `mod(uint256 a, uint256 b, string errorMessage) → uint256` (internal) 107 | 108 | 109 | 110 | Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 111 | Reverts with custom message when dividing by zero. 112 | Counterpart to Solidity's `%` operator. This function uses a `revert` 113 | opcode (which leaves remaining gas untouched) while Solidity uses an 114 | invalid opcode to revert (consuming all remaining gas). 115 | Requirements: 116 | - The divisor cannot be zero. 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/Uniswap.md: -------------------------------------------------------------------------------- 1 | ## `Uniswap` 2 | 3 | 4 | 5 | 6 | 7 | 8 | ### `factory() → address` (external) 9 | 10 | 11 | 12 | 13 | 14 | ### `addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) → uint256 amountA, uint256 amountB, uint256 liquidity` (external) 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/UniswapPair.md: -------------------------------------------------------------------------------- 1 | ## `UniswapPair` 2 | 3 | 4 | 5 | 6 | 7 | 8 | ### `transfer(address to, uint256 value) → bool` (external) 9 | 10 | 11 | 12 | 13 | 14 | ### `transferFrom(address from, address to, uint256 value) → bool` (external) 15 | 16 | 17 | 18 | 19 | 20 | ### `balanceOf(address account) → uint256` (external) 21 | 22 | 23 | 24 | 25 | 26 | ### `approve(address spender, uint256 amount) → bool` (external) 27 | 28 | 29 | 30 | 31 | 32 | ### `totalSupply() → uint256` (external) 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/WETH9.md: -------------------------------------------------------------------------------- 1 | ## `WETH9` 2 | 3 | 4 | 5 | 6 | 7 | 8 | ### `deposit()` (external) 9 | 10 | 11 | 12 | 13 | 14 | ### `balanceOf(address account) → uint256` (external) 15 | 16 | 17 | 18 | 19 | 20 | ### `approve(address spender, uint256 amount) → bool` (external) 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /scripts/HegicPoolKeeper.js: -------------------------------------------------------------------------------- 1 | var Tx = require('ethereumjs-tx').Transaction; 2 | const Web3 = require('web3'); 3 | const provider = new Web3.providers.HttpProvider(); 4 | const web3 = new Web3(provider); 5 | const axios = require('axios').default; 6 | 7 | const account = ; 8 | web3.eth.defaultAccount = account; 9 | 10 | const privateKey = Buffer.from(, 'hex'); 11 | 12 | const abi = [{"inputs":[{"internalType":"address","name":"_keep3r","type":"address"},{"internalType":"address","name":"_hegicPool","type":"address"},{"internalType":"uint256","name":"_minETHRewards","type":"uint256"},{"internalType":"uint256","name":"_minWBTCRewards","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DustSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewards","type":"uint256"}],"name":"ForcedClaimedRewards","type":"event"},{"anonymous":false,"inputs":[],"name":"GovernorAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hegicPool","type":"address"}],"name":"HegicPoolSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"keep3r","type":"address"}],"name":"Keep3rSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"eth","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wbtc","type":"uint256"}],"name":"LotsBought","type":"event"},{"anonymous":false,"inputs":[],"name":"ManagerAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_minETHRewards","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_minWBTCRewards","type":"uint256"}],"name":"MinRewardsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingGovernor","type":"address"}],"name":"PendingGovernorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingManager","type":"address"}],"name":"PendingManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewards","type":"uint256"}],"name":"RewardsClaimedByKeeper","type":"event"},{"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HegicPool","outputs":[{"internalType":"contract IHegicPoolV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_eth","type":"uint256"},{"internalType":"uint256","name":"_wbtc","type":"uint256"}],"name":"buyLots","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forceClaimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"governor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keep3r","outputs":[{"internalType":"contract IKeep3rV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minETHRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minWBTCRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"sendDust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_hegicPool","type":"address"}],"name":"setHegicPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keep3r","type":"address"}],"name":"setKeep3r","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minETHRewards","type":"uint256"},{"internalType":"uint256","name":"_minWBTCRewards","type":"uint256"}],"name":"setMinRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pendingGovernor","type":"address"}],"name":"setPendingGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pendingManager","type":"address"}],"name":"setPendingManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"workable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]; 13 | 14 | const address = "0x5DDe926b0A31346f2485900C5e64c2577F43F774"; 15 | const job = new web3.eth.Contract(abi, address) 16 | 17 | _getGasPrice() 18 | 19 | function _getGasPrice() { 20 | axios.get('https://gasprice.poa.network/') 21 | .then((resp) => { 22 | job.methods.workable().call({from: account}, function(error, result) { 23 | if (result) { 24 | work(resp.data.instant); 25 | } else { 26 | setTimeout(_getGasPrice, 300000); 27 | } 28 | }); 29 | }) 30 | .catch((err) => { 31 | console.log(err); 32 | setTimeout(_getGasPrice, 300000); 33 | }) 34 | } 35 | 36 | function work(gwei) { 37 | web3.eth.getTransactionCount(account, (err, txCount) => { 38 | // Build the transaction 39 | const txObject = { 40 | nonce: web3.utils.toHex(txCount), 41 | to: address, 42 | value: web3.utils.toHex(web3.utils.toWei('0', 'ether')), 43 | gasLimit: web3.utils.toHex(1000000), 44 | gasPrice: web3.utils.toHex(web3.utils.toWei(''+gwei, 'gwei')), 45 | data: job.methods.claimRewards().encodeABI() 46 | } 47 | // Sign the transaction 48 | const tx = new Tx(txObject); 49 | tx.sign(privateKey); 50 | 51 | const serializedTx = tx.serialize(); 52 | const raw = '0x' + serializedTx.toString('hex'); 53 | 54 | // Broadcast the transaction 55 | const transaction = web3.eth.sendSignedTransaction(raw, (err, tx) => { 56 | console.log(tx) 57 | }); 58 | }); 59 | setTimeout(_getGasPrice, 300000); 60 | } 61 | -------------------------------------------------------------------------------- /scripts/Keep3rV1Oracle.js: -------------------------------------------------------------------------------- 1 | var Tx = require('ethereumjs-tx').Transaction; 2 | const Web3 = require('web3'); 3 | const provider = new Web3.providers.HttpProvider(); 4 | const web3 = new Web3(provider); 5 | const axios = require('axios').default; 6 | 7 | const account = ; 8 | web3.eth.defaultAccount = account; 9 | 10 | const privateKey = Buffer.from(, 'hex'); 11 | 12 | const abi = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"KP3R","outputs":[{"internalType":"contract IKeep3rV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNI","outputs":[{"internalType":"contract IUniswapV2Router","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract WETH9","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"add","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_vol","type":"uint256"},{"internalType":"uint256","name":"_underlying","type":"uint256"},{"internalType":"uint256","name":"_time","type":"uint256"}],"name":"blackScholesEstimate","outputs":[{"internalType":"uint256","name":"estimate","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"current","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"points","type":"uint256"}],"name":"daily","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"points","type":"uint256"}],"name":"hourly","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"lastObservation","outputs":[{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"price0Cumulative","type":"uint256"},{"internalType":"uint256","name":"price1Cumulative","type":"uint256"}],"internalType":"struct Keep3rV1Oracle.Observation","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minKeep","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"observationLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"observations","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"price0Cumulative","type":"uint256"},{"internalType":"uint256","name":"price1Cumulative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"pairFor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"}],"name":"pairForWETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"pairs","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingGovernance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"points","type":"uint256"}],"name":"prices","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"granularity","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"points","type":"uint256"},{"internalType":"uint256","name":"window","type":"uint256"}],"name":"realizedVolatility","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"realizedVolatilityDaily","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"realizedVolatilityHourly","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"realizedVolatilityWeekly","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_numbers","type":"uint256[]"},{"internalType":"uint256","name":"_underlying","type":"uint256"},{"internalType":"uint256","name":"_time","type":"uint256"}],"name":"retBasedBlackScholesEstimate","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"points","type":"uint256"},{"internalType":"uint256","name":"window","type":"uint256"}],"name":"sample","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_keep","type":"uint256"}],"name":"setMinKeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"sqrt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"numbers","type":"uint256[]"}],"name":"stddev","outputs":[{"internalType":"uint256","name":"sd","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"update","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"updateFor","outputs":[{"internalType":"bool","name":"updated","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"updatePair","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"points","type":"uint256"}],"name":"weekly","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"work","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"workForFree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"workable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]; 13 | 14 | const address = "0x73353801921417F465377c8d898c6f4C0270282C"; 15 | const oracle = new web3.eth.Contract(abi, address) 16 | 17 | _getGasPrice() 18 | 19 | function _getGasPrice() { 20 | axios.get('https://gasprice.poa.network/') 21 | .then((resp) => { 22 | oracle.methods.workable().call({from: account}, function(error, result) { 23 | if (result) { 24 | update(resp.data.instant); 25 | } else { 26 | setTimeout(_getGasPrice, 300000); 27 | } 28 | }); 29 | }) 30 | .catch((err) => { 31 | console.log(err); 32 | setTimeout(_getGasPrice, 300000); 33 | }) 34 | } 35 | 36 | function update(gwei) { 37 | web3.eth.getTransactionCount(account, (err, txCount) => { 38 | // Build the transaction 39 | const txObject = { 40 | nonce: web3.utils.toHex(txCount), 41 | to: address, 42 | value: web3.utils.toHex(web3.utils.toWei('0', 'ether')), 43 | gasLimit: web3.utils.toHex(1000000), 44 | gasPrice: web3.utils.toHex(web3.utils.toWei(''+gwei, 'gwei')), 45 | data: oracle.methods.work().encodeABI() 46 | } 47 | // Sign the transaction 48 | const tx = new Tx(txObject); 49 | tx.sign(privateKey); 50 | 51 | const serializedTx = tx.serialize(); 52 | const raw = '0x' + serializedTx.toString('hex'); 53 | 54 | // Broadcast the transaction 55 | const transaction = web3.eth.sendSignedTransaction(raw, (err, tx) => { 56 | console.log(tx) 57 | }); 58 | }); 59 | setTimeout(_getGasPrice, 300000); 60 | } 61 | -------------------------------------------------------------------------------- /scripts/MMStrategyKeeperV2.js: -------------------------------------------------------------------------------- 1 | // 2 | // Sample keeper script for running MMStrategyKeeperV2 jobs in Keep3r network 3 | // please resort to https://docs.ethers.io/v5/ for more details on the library usage 4 | // 5 | const { 6 | ethers 7 | } = require("ethers"); 8 | 9 | const provider = new ethers.providers.JsonRpcProvider(""); 10 | w = new ethers.Wallet("", provider); 11 | 12 | const mm_kp3rv2_abi = '[{"inputs":[{"internalType":"address","name":"_keep3r","type":"address"},{"internalType":"address","name":"_keep3rHelper","type":"address"},{"internalType":"address","name":"_slidingOracle","type":"address"},{"internalType":"address","name":"_sushiSlidingOracle","type":"address"},{"internalType":"address","name":"_mmController","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"_requiredEarnBalance","type":"uint256"}],"name":"EarnVaultAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"_requiredEarnBalance","type":"uint256"}],"name":"EarnVaultModified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_vault","type":"address"}],"name":"EarnVaultRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_vault","type":"address"},{"indexed":false,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_requiredHarvest","type":"uint256"},{"indexed":false,"internalType":"bool","name":"_requiredKeepMinRatio","type":"bool"},{"indexed":false,"internalType":"bool","name":"_requiredLeverageToMax","type":"bool"},{"indexed":false,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"yieldTokenOracle","type":"uint256"}],"name":"HarvestStrategyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"_requiredHarvest","type":"uint256"}],"name":"HarvestStrategyModified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_strategy","type":"address"}],"name":"HarvestStrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"profitTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"profitFactor","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"profitInEther","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethCallCost","type":"uint256"}],"name":"HarvestableCheck","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_strategy","type":"address"}],"name":"HarvestedByKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"keep3rHelper","type":"address"}],"name":"Keep3rHelperSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"keep3r","type":"address"}],"name":"Keep3rSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"slidingOracle","type":"address"}],"name":"SlidingOracleSet","type":"event"},{"inputs":[],"name":"COMP","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CRV","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CRVRENWBTC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DAI","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KP3R","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LINK","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIRUSTLP","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUSHISWAP_ORACLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"THREECRV","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_ORACLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WBTC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ZRX","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_governor","type":"address"}],"name":"_setGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_requiredHarvest","type":"uint256"},{"internalType":"bool","name":"_requiredKeepMinRatio","type":"bool"},{"internalType":"bool","name":"_requiredLeverageToMax","type":"bool"},{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"yieldTokenOracle","type":"uint256"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"uint256","name":"_requiredEarnBalance","type":"uint256"}],"name":"addVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"earn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"earnable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollateralizedStrategies","outputs":[{"internalType":"address[]","name":"_strategies","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStrategies","outputs":[{"internalType":"address[]","name":"_strategies","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaults","outputs":[{"internalType":"address[]","name":"_vaults","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"harvestable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"keep3r","outputs":[{"internalType":"contract IKeep3rV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keep3rHelper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"keepMinRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"keepMinRatioMayday","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minHarvestInterval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mmController","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"profitFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"removeEarnVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"removeHarvestStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"requiredEarnBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"requiredHarvest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_keep3r","type":"address"}],"name":"setKeep3r","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keep3rHelper","type":"address"}],"name":"setKeep3rHelper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_interval","type":"uint256"}],"name":"setMinHarvestInterval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_profitFactor","type":"uint256"}],"name":"setProfitFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_slidingOracle","type":"address"}],"name":"setSlidingOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sushiSlidingOracle","type":"address"}],"name":"setSushiSlidingOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slidingOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stratagyYieldTokenOracles","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stratagyYieldTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"strategyLastHarvest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sushiSlidingOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"uint256","name":"_requiredEarnBalance","type":"uint256"}],"name":"updateRequiredEarn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_requiredHarvest","type":"uint256"}],"name":"updateRequiredHarvestAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_yieldTokenOracle","type":"uint256"}],"name":"updateYieldTokenOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"vaultStrategies","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]'; 13 | 14 | const mm_kp3r_v2 = "0x0bD1d668d8E83d14252F2e01D5873df77A6511f0"; 15 | const mmKp3rV2 = new ethers.Contract(mm_kp3r_v2, mm_kp3rv2_abi, w); 16 | 17 | async function kp3r() { 18 | 19 | await kp3rWorkEarn(); 20 | await kp3rWorkHarvest(); 21 | await kp3rWorkKeepMinRatio(); 22 | 23 | } 24 | 25 | ///////////////////////////////////////////////////////////////////////////// 26 | // Mushrooms KP3R V2 Job 27 | ///////////////////////////////////////////////////////////////////////////// 28 | 29 | async function kp3rv2Earnable(_contract, _vault) { 30 | let result = await _contract.earnable(_vault) 31 | console.log('earnable() check for ' + _vault + '=' + result); 32 | if (result == 'true') { 33 | kp3rv2Earn(_contract, _vault); 34 | } 35 | } 36 | 37 | async function kp3rv2Earn(_contract, _vault) { 38 | let result = await _contract.earn(_vault); 39 | console.log('submitted earn() for ' + result); 40 | } 41 | 42 | async function kp3rWorkEarn() { 43 | console.log("--------Mushrooms KP3R V2 Job: earn()--------"); 44 | mmKp3rV2.getVaults().then((result) => { 45 | for (i = 0; i < result.length; i++) { 46 | kp3rv2Earnable(mmKp3rV2, result[i]); 47 | } 48 | }).catch(console.error); 49 | } 50 | 51 | async function kp3rv2Harvestable(_contract, _strategy) { 52 | let result = await _contract.callStatic.harvestable(_strategy); 53 | console.log('harvestable() check for ' + _strategy + '=' + result); 54 | if (result == 'true') { 55 | kp3rv2Harvest(_contract, _strategy); 56 | } 57 | } 58 | 59 | async function kp3rv2Harvest(_contract, _strategy) { 60 | let result = await _contract.harvest(_strategy); 61 | console.log('submitted harvest() for ' + result); 62 | } 63 | 64 | async function kp3rWorkHarvest() { 65 | console.log("--------Mushrooms KP3R V2 Job: harvest()--------"); 66 | mmKp3rV2.getStrategies().then((result) => { 67 | for (i = 0; i < result.length; i++) { 68 | kp3rv2Harvestable(mmKp3rV2, result[i]); 69 | } 70 | }).catch(console.error); 71 | } 72 | 73 | async function kp3rv2KeepMinRatioMayday(_contract, _strategy) { 74 | let result = await _contract.keepMinRatioMayday(_strategy); 75 | console.log('keepMinRatioMayday() check for ' + _strategy + '=' + result); 76 | if (result == 'true') { 77 | kp3rv2KeepMinRatio(_contract, _strategy); 78 | } 79 | } 80 | 81 | async function kp3rv2KeepMinRatio(_contract, _strategy) { 82 | let result = await _contract.keepMinRatio(_strategy); 83 | console.log('submitted keepMinRatioMayday() for ' + result); 84 | } 85 | 86 | async function kp3rWorkKeepMinRatio() { 87 | console.log("--------Mushrooms KP3R V2 Job: keepMinRatio()--------"); 88 | mmKp3rV2.getCollateralizedStrategies().then((result) => { 89 | for (i = 0; i < result.length; i++) { 90 | kp3rv2KeepMinRatioMayday(mmKp3rV2, result[i]); 91 | } 92 | }).catch(console.error); 93 | } 94 | 95 | kp3r(); -------------------------------------------------------------------------------- /scripts/UniswapV2OracleKeeper.js: -------------------------------------------------------------------------------- 1 | var Tx = require('ethereumjs-tx').Transaction; 2 | const Web3 = require('web3'); 3 | const provider = new Web3.providers.HttpProvider(); 4 | const web3 = new Web3(provider); 5 | const axios = require('axios').default; 6 | 7 | const account = ; 8 | web3.eth.defaultAccount = account; 9 | 10 | const privateKey = Buffer.from(, 'hex'); 11 | 12 | const abi = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"KP3R","outputs":[{"internalType":"contract IKeep3rV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"add","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"current","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"lastObservation","outputs":[{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"price0Cumulative","type":"uint256"},{"internalType":"uint256","name":"price1Cumulative","type":"uint256"}],"internalType":"struct UniswapV2Oracle.Observation","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"pairObservations","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"price0Cumulative","type":"uint256"},{"internalType":"uint256","name":"price1Cumulative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pairs","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingGovernance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"granularity","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"update","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"updateFor","outputs":[{"internalType":"bool","name":"updated","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"updatePair","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"work","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"workForFree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"workable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"workable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}] 13 | 14 | const address = "0xca2e2df6a7a7cf5bd19d112e8568910a6c2d3885"; 15 | const oracle = new web3.eth.Contract(abi, address) 16 | 17 | _getGasPrice() 18 | 19 | function _getGasPrice() { 20 | axios.get('https://gasprice.poa.network/') 21 | .then((resp) => { 22 | oracle.methods.workable().call({from: account}, function(error, result) { 23 | if (result) { 24 | update(resp.data.instant); 25 | } else { 26 | setTimeout(_getGasPrice, 300000); 27 | } 28 | }); 29 | }) 30 | .catch((err) => { 31 | console.log(err); 32 | setTimeout(_getGasPrice, 300000); 33 | }) 34 | } 35 | 36 | function update(gwei) { 37 | web3.eth.getTransactionCount(account, (err, txCount) => { 38 | // Build the transaction 39 | const txObject = { 40 | nonce: web3.utils.toHex(txCount), 41 | to: address, 42 | value: web3.utils.toHex(web3.utils.toWei('0', 'ether')), 43 | gasLimit: web3.utils.toHex(1000000), 44 | gasPrice: web3.utils.toHex(web3.utils.toWei(''+gwei, 'gwei')), 45 | data: oracle.methods.work().encodeABI() 46 | } 47 | // Sign the transaction 48 | const tx = new Tx(txObject); 49 | tx.sign(privateKey); 50 | 51 | const serializedTx = tx.serialize(); 52 | const raw = '0x' + serializedTx.toString('hex'); 53 | 54 | // Broadcast the transaction 55 | const transaction = web3.eth.sendSignedTransaction(raw, (err, tx) => { 56 | console.log(tx) 57 | }); 58 | }); 59 | setTimeout(_getGasPrice, 300000); 60 | } 61 | -------------------------------------------------------------------------------- /scripts/YearnV1EarnKeeper.js: -------------------------------------------------------------------------------- 1 | var Tx = require('ethereumjs-tx').Transaction; 2 | const Web3 = require('web3'); 3 | const provider = new Web3.providers.HttpProvider(); 4 | const web3 = new Web3(provider); 5 | const axios = require('axios').default; 6 | 7 | const account = ; 8 | web3.eth.defaultAccount = account; 9 | 10 | const privateKey = Buffer.from(, 'hex'); 11 | 12 | const abi = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KP3R","outputs":[{"internalType":"contract IKeep3rV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"THRESHOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IYERC20","name":"_token","type":"address"}],"name":"shouldRebalance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokens","outputs":[{"internalType":"contract IYERC20[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"work","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"workable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]; 13 | 14 | const address = "0xe7F4ab593aeC81EcA754Da1B3B7cE0C42a13Ec0C"; 15 | const job = new web3.eth.Contract(abi, address) 16 | 17 | _getGasPrice() 18 | 19 | function _getGasPrice() { 20 | axios.get('https://gasprice.poa.network/') 21 | .then((resp) => { 22 | job.methods.workable().call({from: account}, function(error, result) { 23 | if (result) { 24 | work(resp.data.instant); 25 | } else { 26 | setTimeout(_getGasPrice, 300000); 27 | } 28 | }); 29 | }) 30 | .catch((err) => { 31 | console.log(err); 32 | setTimeout(_getGasPrice, 300000); 33 | }) 34 | } 35 | 36 | function work(gwei) { 37 | web3.eth.getTransactionCount(account, (err, txCount) => { 38 | // Build the transaction 39 | const txObject = { 40 | nonce: web3.utils.toHex(txCount), 41 | to: address, 42 | value: web3.utils.toHex(web3.utils.toWei('0', 'ether')), 43 | gasLimit: web3.utils.toHex(1000000), 44 | gasPrice: web3.utils.toHex(web3.utils.toWei(''+gwei, 'gwei')), 45 | data: job.methods.work().encodeABI() 46 | } 47 | // Sign the transaction 48 | const tx = new Tx(txObject); 49 | tx.sign(privateKey); 50 | 51 | const serializedTx = tx.serialize(); 52 | const raw = '0x' + serializedTx.toString('hex'); 53 | 54 | // Broadcast the transaction 55 | const transaction = web3.eth.sendSignedTransaction(raw, (err, tx) => { 56 | console.log(tx) 57 | }); 58 | }); 59 | setTimeout(_getGasPrice, 300000); 60 | } 61 | -------------------------------------------------------------------------------- /test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keep3r-network/keep3r.network/a6897007db6e656e0e310ed7ee4ad42904fe2794/test/.gitkeep -------------------------------------------------------------------------------- /test/CompoundFlashLiquidationTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./script.sol"; 5 | import "./CompoundFlashLiquidation.sol"; 6 | 7 | contract CompoundFlashLiquidationTest is script { 8 | 9 | CompoundFlashLiquidate private CFL; 10 | ERC20Like private WETH9 = ERC20Like(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 11 | 12 | 13 | function run() public { 14 | run(this.work).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 15 | } 16 | 17 | function work() external { 18 | CFL = new CompoundFlashLiquidate(); 19 | 20 | address underlying = CFL.underlying(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); 21 | address pair = CFL.underlyingPair(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); 22 | uint liquidatable = CFL.liquidatable(0x6cE3DfE03066eC06658F6F9585C34C3f71C71fC4, 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); 23 | 24 | fmt.printf("underlying()=%a\n",abi.encode(underlying)); 25 | fmt.printf("pair()=%a\n",abi.encode(pair)); 26 | fmt.printf("liquidatable()=%u\n",abi.encode(liquidatable)); 27 | 28 | CFL.liquidate(0x6cE3DfE03066eC06658F6F9585C34C3f71C71fC4, 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643, 0x6C8c6b02E7b2BE14d4fA6022Dfd6d75921D90E4E); 29 | //CFL.liquidateCalculated(address borrower, IUniswapV2Pair pair, address underlying, uint amount, address borrowed, address supplied) 30 | 31 | /* 32 | 33 | CFL.liquidateCalculated(0x012916eD48b20433B698CB32b488E52C639a2A66, 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,72855155,0x39AA39c021dfbaE8faC545936693aC917d5E7563,0x6C8c6b02E7b2BE14d4fA6022Dfd6d75921D90E4E); 34 | */ 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/CreamFlashLiquidationTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./script.sol"; 5 | import "./CreamFlashLiquidation.sol"; 6 | 7 | contract CreamFlashLiquidationTest is script { 8 | 9 | CreamFlashLiquidate private CFL; 10 | ERC20Like private WETH9 = ERC20Like(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 11 | 12 | 13 | function run() public { 14 | run(this.work).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 15 | } 16 | 17 | function work() external { 18 | CFL = new CreamFlashLiquidate(); 19 | 20 | CFL.liquidate(0xe024A6d8B1A76C746F0E42546BBa6A52a74769Ec , 0x797AAB1ce7c01eB727ab980762bA88e7133d2157, 0x797AAB1ce7c01eB727ab980762bA88e7133d2157); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/Keep3rTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./script.sol"; 5 | 6 | library SafeMath { 7 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 8 | uint256 c = a + b; 9 | require(c >= a, "SafeMath: addition overflow"); 10 | 11 | return c; 12 | } 13 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 14 | return sub(a, b, "SafeMath: subtraction overflow"); 15 | } 16 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 17 | require(b <= a, errorMessage); 18 | uint256 c = a - b; 19 | 20 | return c; 21 | } 22 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 23 | if (a == 0) { 24 | return 0; 25 | } 26 | 27 | uint256 c = a * b; 28 | require(c / a == b, "SafeMath: multiplication overflow"); 29 | 30 | return c; 31 | } 32 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 33 | return div(a, b, "SafeMath: division by zero"); 34 | } 35 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 36 | // Solidity only automatically asserts when dividing by 0 37 | require(b > 0, errorMessage); 38 | uint256 c = a / b; 39 | 40 | return c; 41 | } 42 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 43 | return mod(a, b, "SafeMath: modulo by zero"); 44 | } 45 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 46 | require(b != 0, errorMessage); 47 | return a % b; 48 | } 49 | } 50 | 51 | interface Keep3rLike { 52 | function setup() external payable; 53 | function balanceOf(address) external view returns (uint); 54 | function liquidity() external view returns (address); 55 | 56 | function bond(uint) external; 57 | function activate() external; 58 | function unbond() external; 59 | function withdraw() external; 60 | function claim() external; 61 | 62 | function isKeeper(address) external view returns (bool); 63 | 64 | function addJob(address) external; 65 | function removeJob(address) external; 66 | function jobs(address) external view returns (bool); 67 | 68 | function workReceipt(address,uint) external; 69 | 70 | function submitJob(address,uint) external; 71 | } 72 | 73 | contract Keep3rTest is script { 74 | using SafeMath for uint; 75 | 76 | Keep3rLike constant private KPR = Keep3rLike(0x4fF0170A2bf39368681109034A2F7d505f181544); 77 | 78 | function run() public { 79 | run(this.init).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 80 | run(this.bond).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 81 | advanceBlocks(3 days); 82 | run(this.activate).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 83 | run(this.unbond).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 84 | advanceBlocks(14 days); 85 | run(this.withdraw).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 86 | run(this.manageJobs).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 87 | run(this.submitJob).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 88 | run(this.work).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 89 | run(this.claim).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 90 | } 91 | 92 | function submitJob() external { 93 | fmt.printf("liquidity=%.18u\n",abi.encode(ERC20Like(KPR.liquidity()).balanceOf(address(this)))); 94 | ERC20Like(KPR.liquidity()).approve(address(KPR), uint(-1)); 95 | KPR.submitJob(address(this), ERC20Like(KPR.liquidity()).balanceOf(address(this))); 96 | fmt.printf("liquidity=%.18u\n",abi.encode(ERC20Like(KPR.liquidity()).balanceOf(address(this)))); 97 | } 98 | 99 | function work() external { 100 | KPR.workReceipt(address(this), 1e18); 101 | } 102 | 103 | function manageJobs() external { 104 | KPR.addJob(address(this)); 105 | KPR.removeJob(address(this)); 106 | fmt.printf("isJob=%b\n",abi.encode(KPR.jobs(address(this)))); 107 | KPR.addJob(address(this)); 108 | fmt.printf("isJob=%b\n",abi.encode(KPR.jobs(address(this)))); 109 | } 110 | 111 | function init() external { 112 | fmt.printf("balanceOf=%.18u\n",abi.encode(KPR.balanceOf(address(this)))); 113 | KPR.setup.value(1e18)(); 114 | fmt.printf("liquidity=%a\n",abi.encode(KPR.liquidity())); 115 | } 116 | 117 | function bond() external { 118 | fmt.printf("balanceOf=%.18u\n",abi.encode(KPR.balanceOf(address(this)))); 119 | KPR.bond(KPR.balanceOf(address(this))); 120 | fmt.printf("balanceOf=%.18u\n",abi.encode(KPR.balanceOf(address(this)))); 121 | } 122 | 123 | function activate() external { 124 | KPR.activate(); 125 | fmt.printf("isKeeper=%b\n",abi.encode(KPR.isKeeper(address(this)))); 126 | } 127 | 128 | function unbond() external { 129 | KPR.unbond(); 130 | fmt.printf("isKeeper=%b\n",abi.encode(KPR.isKeeper(address(this)))); 131 | } 132 | 133 | function withdraw() external { 134 | fmt.printf("balanceOf=%.18u\n",abi.encode(KPR.balanceOf(address(this)))); 135 | KPR.withdraw(); 136 | fmt.printf("balanceOf=%.18u\n",abi.encode(KPR.balanceOf(address(this)))); 137 | } 138 | 139 | function claim() external { 140 | fmt.printf("balanceOf=%.18u\n",abi.encode(KPR.balanceOf(address(this)))); 141 | KPR.claim(); 142 | fmt.printf("balanceOf=%.18u\n",abi.encode(KPR.balanceOf(address(this)))); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /test/MetaKeep3rTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./script.sol"; 5 | import "./MetaKeep3r.sol"; 6 | 7 | interface Keep3rV1Like { 8 | function addVotes(address voter, uint amount) external; 9 | function addJob(address job) external; 10 | function addKPRCredit(address job, uint amount) external; 11 | function isKeeper(address keeper) external returns (bool); 12 | function balanceOf(address keeper) external returns (uint); 13 | function worked(address keeper) external; 14 | function credits(address job, address credit) external view returns (uint); 15 | function bonds(address keeper, address credit) external view returns (uint); 16 | } 17 | 18 | interface MetaKeep3rLike { 19 | function work(address job) external; 20 | function unbond() external; 21 | function withdraw() external; 22 | } 23 | 24 | contract TestJob { 25 | Keep3rV1Like constant private KPR = Keep3rV1Like(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 26 | function work() external { 27 | require(KPR.isKeeper(msg.sender), "TestJob::work(): !keeper"); 28 | KPR.worked(msg.sender); 29 | } 30 | } 31 | 32 | contract MetaKeep3rTest is script { 33 | using SafeMath for uint; 34 | 35 | Keep3rV1Like constant private KPR = Keep3rV1Like(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 36 | MetaKeep3rLike private MK; 37 | address private JOB; 38 | 39 | function run() public { 40 | run(this.init).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 41 | run(this.work).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 42 | run(this.unbond).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 43 | advanceBlocks(100000); 44 | run(this.withdraw).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 45 | } 46 | 47 | function init() external { 48 | JOB = address(new TestJob()); 49 | MK = MetaKeep3rLike(address(new MetaKeep3r())); 50 | 51 | KPR.addVotes(address(MK), 0); 52 | 53 | KPR.addJob(address(MK)); 54 | KPR.addKPRCredit(address(MK),10e18); 55 | 56 | KPR.addJob(JOB); 57 | KPR.addKPRCredit(JOB,10e18); 58 | } 59 | 60 | function work() external { 61 | fmt.printf("JOB=%a\n",abi.encode(JOB)); 62 | fmt.printf("ETH=%.18u\n",abi.encode(address(this).balance)); 63 | MK.work(JOB); 64 | fmt.printf("ETH=%.18u\n",abi.encode(address(this).balance)); 65 | 66 | 67 | fmt.printf("credits(JOB)=%.18u\n",abi.encode(KPR.credits(JOB, address(KPR)))); 68 | fmt.printf("bonds(MK)=%.18u\n",abi.encode(KPR.bonds(address(MK), address(KPR)))); 69 | fmt.printf("credits(MK)=%.18u\n",abi.encode(KPR.credits(address(MK), address(KPR)))); 70 | } 71 | 72 | function unbond() external { 73 | fmt.printf("bonds(MK)=%.18u\n",abi.encode(KPR.bonds(address(MK), address(KPR)))); 74 | MK.unbond(); 75 | fmt.printf("bonds(MK)=%.18u\n",abi.encode(KPR.bonds(address(MK), address(KPR)))); 76 | } 77 | 78 | function withdraw() external { 79 | fmt.printf("balanceOf(MK)=%.18u\n",abi.encode(KPR.balanceOf(address(MK)))); 80 | fmt.printf("bonds(MK)=%.18u\n",abi.encode(KPR.bonds(address(MK), address(KPR)))); 81 | MK.withdraw(); 82 | fmt.printf("balanceOf(MK)=%.18u\n",abi.encode(KPR.balanceOf(address(MK)))); 83 | fmt.printf("bonds(MK)=%.18u\n",abi.encode(KPR.bonds(address(MK), address(KPR)))); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/REPL.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./script.sol"; 5 | 6 | contract REPL is script { 7 | 8 | uint private constant FIXED_1 = 0x080000000000000000000000000000000; 9 | uint private constant FIXED_2 = 0x100000000000000000000000000000000; 10 | uint private constant SQRT_1 = 13043817825332782212; 11 | uint private constant LNX = 3988425491; 12 | uint private constant LOG_E_2 = 6931471806; 13 | uint private constant LOG_10_2 = 3010299957; 14 | uint private constant BASE = 1e10; 15 | 16 | function floorLog2(uint256 _n) internal pure returns (uint8) { 17 | uint8 res = 0; 18 | 19 | if (_n < 256) { 20 | // At most 8 iterations 21 | while (_n > 1) { 22 | _n >>= 1; 23 | res += 1; 24 | } 25 | } else { 26 | // Exactly 8 iterations 27 | for (uint8 s = 128; s > 0; s >>= 1) { 28 | if (_n >= (uint(1) << s)) { 29 | _n >>= s; 30 | res |= s; 31 | } 32 | } 33 | } 34 | 35 | return res; 36 | } 37 | 38 | function generalLog(uint256 x) internal pure returns (uint) { 39 | uint res = 0; 40 | 41 | // If x >= 2, then we compute the integer part of log2(x), which is larger than 0. 42 | if (x >= FIXED_2) { 43 | uint8 count = floorLog2(x / FIXED_1); 44 | x >>= count; // now x < 2 45 | res = count * FIXED_1; 46 | } 47 | 48 | // If x > 1, then we compute the fraction part of log2(x), which is larger than 0. 49 | if (x > FIXED_1) { 50 | for (uint8 i = 127; i > 0; --i) { 51 | x = (x * x) / FIXED_1; // now 1 < x < 4 52 | if (x >= FIXED_2) { 53 | x >>= 1; // now 1 < x < 2 54 | res += uint(1) << (i - 1); 55 | } 56 | } 57 | } 58 | 59 | return res * LOG_10_2 / BASE; 60 | } 61 | 62 | function ln(uint256 x) internal pure returns (uint) { 63 | uint res = 0; 64 | 65 | // If x >= 2, then we compute the integer part of log2(x), which is larger than 0. 66 | if (x >= FIXED_2) { 67 | uint8 count = floorLog2(x / FIXED_1); 68 | x >>= count; // now x < 2 69 | res = count * FIXED_1; 70 | } 71 | 72 | // If x > 1, then we compute the fraction part of log2(x), which is larger than 0. 73 | if (x > FIXED_1) { 74 | for (uint8 i = 127; i > 0; --i) { 75 | x = (x * x) / FIXED_1; // now 1 < x < 4 76 | if (x >= FIXED_2) { 77 | x >>= 1; // now 1 < x < 2 78 | res += uint(1) << (i - 1); 79 | } 80 | } 81 | } 82 | 83 | return res * LOG_E_2 / BASE; 84 | } 85 | 86 | function sqrt(uint x) internal pure returns (uint y) { 87 | uint z = (x + 1) / 2; 88 | y = x; 89 | while (z < y) { 90 | y = z; 91 | z = (x / z + z) / 2; 92 | } 93 | } 94 | 95 | function stddev(uint[] memory numbers) public pure returns (uint sd, uint mean) { 96 | uint sum = 0; 97 | for(uint i = 0; i < numbers.length; i++) { 98 | sum += numbers[i]; 99 | } 100 | mean = sum / numbers.length; // Integral value; float not supported in Solidity 101 | sum = 0; 102 | uint i; 103 | for(i = 0; i < numbers.length; i++) { 104 | sum += (numbers[i] - mean) ** 2; 105 | } 106 | sd = sqrt(sum / (numbers.length - 1)); //Integral value; float not supported in Solidity 107 | return (sd, mean); 108 | } 109 | 110 | /** 111 | * @dev computes e ^ (x / FIXED_1) * FIXED_1 112 | * input range: 0 <= x <= OPT_EXP_MAX_VAL - 1 113 | * auto-generated via 'PrintFunctionOptimalExp.py' 114 | * Detailed description: 115 | * - Rewrite the input as a sum of binary exponents and a single residual r, as small as possible 116 | * - The exponentiation of each binary exponent is given (pre-calculated) 117 | * - The exponentiation of r is calculated via Taylor series for e^x, where x = r 118 | * - The exponentiation of the input is calculated by multiplying the intermediate results above 119 | * - For example: e^5.521692859 = e^(4 + 1 + 0.5 + 0.021692859) = e^4 * e^1 * e^0.5 * e^0.021692859 120 | */ 121 | function optimalExp(uint256 x) internal pure returns (uint256) { 122 | uint256 res = 0; 123 | 124 | uint256 y; 125 | uint256 z; 126 | 127 | z = y = x % 0x10000000000000000000000000000000; // get the input modulo 2^(-3) 128 | z = (z * y) / FIXED_1; 129 | res += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!) 130 | z = (z * y) / FIXED_1; 131 | res += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!) 132 | z = (z * y) / FIXED_1; 133 | res += z * 0x0168244fdac78000; // add y^04 * (20! / 04!) 134 | z = (z * y) / FIXED_1; 135 | res += z * 0x004807432bc18000; // add y^05 * (20! / 05!) 136 | z = (z * y) / FIXED_1; 137 | res += z * 0x000c0135dca04000; // add y^06 * (20! / 06!) 138 | z = (z * y) / FIXED_1; 139 | res += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!) 140 | z = (z * y) / FIXED_1; 141 | res += z * 0x000036e0f639b800; // add y^08 * (20! / 08!) 142 | z = (z * y) / FIXED_1; 143 | res += z * 0x00000618fee9f800; // add y^09 * (20! / 09!) 144 | z = (z * y) / FIXED_1; 145 | res += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!) 146 | z = (z * y) / FIXED_1; 147 | res += z * 0x0000000e30dce400; // add y^11 * (20! / 11!) 148 | z = (z * y) / FIXED_1; 149 | res += z * 0x000000012ebd1300; // add y^12 * (20! / 12!) 150 | z = (z * y) / FIXED_1; 151 | res += z * 0x0000000017499f00; // add y^13 * (20! / 13!) 152 | z = (z * y) / FIXED_1; 153 | res += z * 0x0000000001a9d480; // add y^14 * (20! / 14!) 154 | z = (z * y) / FIXED_1; 155 | res += z * 0x00000000001c6380; // add y^15 * (20! / 15!) 156 | z = (z * y) / FIXED_1; 157 | res += z * 0x000000000001c638; // add y^16 * (20! / 16!) 158 | z = (z * y) / FIXED_1; 159 | res += z * 0x0000000000001ab8; // add y^17 * (20! / 17!) 160 | z = (z * y) / FIXED_1; 161 | res += z * 0x000000000000017c; // add y^18 * (20! / 18!) 162 | z = (z * y) / FIXED_1; 163 | res += z * 0x0000000000000014; // add y^19 * (20! / 19!) 164 | z = (z * y) / FIXED_1; 165 | res += z * 0x0000000000000001; // add y^20 * (20! / 20!) 166 | res = res / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0! 167 | 168 | if ((x & 0x010000000000000000000000000000000) != 0) 169 | res = (res * 0x1c3d6a24ed82218787d624d3e5eba95f9) / 0x18ebef9eac820ae8682b9793ac6d1e776; // multiply by e^2^(-3) 170 | if ((x & 0x020000000000000000000000000000000) != 0) 171 | res = (res * 0x18ebef9eac820ae8682b9793ac6d1e778) / 0x1368b2fc6f9609fe7aceb46aa619baed4; // multiply by e^2^(-2) 172 | if ((x & 0x040000000000000000000000000000000) != 0) 173 | res = (res * 0x1368b2fc6f9609fe7aceb46aa619baed5) / 0x0bc5ab1b16779be3575bd8f0520a9f21f; // multiply by e^2^(-1) 174 | if ((x & 0x080000000000000000000000000000000) != 0) 175 | res = (res * 0x0bc5ab1b16779be3575bd8f0520a9f21e) / 0x0454aaa8efe072e7f6ddbab84b40a55c9; // multiply by e^2^(+0) 176 | if ((x & 0x100000000000000000000000000000000) != 0) 177 | res = (res * 0x0454aaa8efe072e7f6ddbab84b40a55c5) / 0x00960aadc109e7a3bf4578099615711ea; // multiply by e^2^(+1) 178 | if ((x & 0x200000000000000000000000000000000) != 0) 179 | res = (res * 0x00960aadc109e7a3bf4578099615711d7) / 0x0002bf84208204f5977f9a8cf01fdce3d; // multiply by e^2^(+2) 180 | if ((x & 0x400000000000000000000000000000000) != 0) 181 | res = (res * 0x0002bf84208204f5977f9a8cf01fdc307) / 0x0000003c6ab775dd0b95b4cbee7e65d11; // multiply by e^2^(+3) 182 | 183 | return res; 184 | } 185 | 186 | function ncdf(uint x) internal pure returns (uint) { 187 | int t1 = int(1e7 + (2315419 * x / FIXED_1)); 188 | uint exp = x / 2 * x / FIXED_1; 189 | int d = int(3989423 * FIXED_1 / optimalExp(uint(exp))); 190 | uint prob = uint(d * (3193815 + ( -3565638 + (17814780 + (-18212560 + 13302740 * 1e7 / t1) * 1e7 / t1) * 1e7 / t1) * 1e7 / t1) * 1e7 / t1); 191 | if( x > 0 ) prob = 1e14 - prob; 192 | return prob; 193 | } 194 | 195 | function vol(uint[] memory p) internal pure returns (uint x) { 196 | for (uint8 i = 1; i <= (p.length-1); i++) { 197 | x += ((generalLog(p[i] * FIXED_1) - generalLog(p[i-1] * FIXED_1)))**2; 198 | } 199 | x = sqrt(uint(252) * sqrt(x / (p.length-1))); 200 | return uint(1e18) * x / SQRT_1; 201 | } 202 | 203 | function run() public { 204 | run(this.repl).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 205 | } 206 | 207 | function C(uint t, uint v, uint sp, uint st) public view returns (uint) { 208 | if (sp == st) { 209 | return LNX * sp / 1e10 * v / 1e18 * sqrt(1e18 * t / 365) / 1e9; 210 | } 211 | uint sigma = ((v**2)/2); 212 | uint sigmaB = 1e36; 213 | 214 | uint sig = 1e18 * sigma / sigmaB * t / 365; 215 | 216 | uint sSQRT = v * sqrt(1e18 * t / 365) / 1e9; 217 | 218 | uint d1 = 1e18 * ln(FIXED_1 * sp / st) / FIXED_1; 219 | d1 = (d1 + sig) * 1e18 / sSQRT; 220 | uint d2 = d1 - sSQRT; 221 | 222 | uint cdfD1 = ncdf(FIXED_1 * d1 / 1e18); 223 | uint cdfD2 = ncdf(FIXED_1 * d2 / 1e18); 224 | 225 | return sp * cdfD1 / 1e14 - st * cdfD2 / 1e14; 226 | } 227 | 228 | function repl() external { 229 | uint t = 28; 230 | uint v = 408663661862467992; 231 | 232 | uint sp = 35971243930840556466; 233 | uint st = 35971243930840556466; 234 | 235 | uint _c; 236 | uint _p; 237 | 238 | if (sp > st) { 239 | _c = C(t, v, sp, st); 240 | _p = st-sp+_c; 241 | } else { 242 | _p = C(t, v, st, sp); 243 | _c = st-sp+_p; 244 | } 245 | 246 | 247 | 248 | 249 | fmt.printf("C=%u\n",abi.encode(_c)); 250 | fmt.printf("P=%u\n",abi.encode(_p)); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /test/SushiswapV2Keep3rTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./script.sol"; 5 | import "./SushibarKeep3r.sol"; 6 | 7 | interface Keep3rV1Like { 8 | function addVotes(address voter, uint amount) external; 9 | function addJob(address job) external; 10 | function addKPRCredit(address job, uint amount) external; 11 | function isKeeper(address keeper) external returns (bool); 12 | function balanceOf(address keeper) external returns (uint); 13 | function worked(address keeper) external; 14 | function credits(address job, address credit) external view returns (uint); 15 | function bonds(address keeper, address credit) external view returns (uint); 16 | } 17 | 18 | interface SushiswapV2Keep3rLike { 19 | function count() external view returns (uint); 20 | function workableAll(uint _count) external view returns (address[] memory); 21 | function batch(address[] calldata pair) external; 22 | function work(address pair) external; 23 | } 24 | 25 | contract TestJob { 26 | Keep3rV1Like constant private KPR = Keep3rV1Like(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 27 | function work() external { 28 | require(KPR.isKeeper(msg.sender), "TestJob::work(): !keeper"); 29 | KPR.worked(msg.sender); 30 | } 31 | } 32 | 33 | contract SushiswapV2Keep3rTest is script { 34 | 35 | Keep3rV1Like constant private KPR = Keep3rV1Like(0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44); 36 | SushiswapV2Keep3rLike private SV2K; 37 | 38 | function run() public { 39 | run(this.init).withCaller(0x2D407dDb06311396fE14D4b49da5F0471447d45C); 40 | run(this.pair).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 41 | run(this.work).withCaller(0x9f6FdC2565CfC9ab8E184753bafc8e94C0F985a0); 42 | } 43 | 44 | function init() external { 45 | SV2K = SushiswapV2Keep3rLike(address(new SushiswapV2Keep3r())); 46 | 47 | KPR.addJob(address(SV2K)); 48 | KPR.addKPRCredit(address(SV2K),10e18); 49 | } 50 | 51 | function pair() external { 52 | SV2K.work(0xc4dE5Cc1232f6493Cc7BF7bcb12F905eb9742Bd7); 53 | } 54 | 55 | function work() external { 56 | uint _count = SV2K.count(); 57 | fmt.printf("count()=%u\n",abi.encode(_count)); 58 | address[] memory _jobs = SV2K.workableAll(_count); 59 | fmt.printf("_jobs.length()=%u\n",abi.encode(_jobs.length)); 60 | SV2K.batch(_jobs); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/script.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | 4 | contract ERC20Like { 5 | function decimals() public returns (uint8); 6 | function name() public returns (string memory); 7 | function symbol() public returns (string memory); 8 | function approve(address,uint) public; 9 | 10 | function approveAndCall(address, uint, bytes memory) public; 11 | 12 | function balanceOf(address) public view returns (uint); 13 | function transfer(address, uint) public; 14 | function transferFrom(address, address, uint) public; 15 | function totalSupply() public returns (uint); 16 | 17 | function mint(address, uint) public; 18 | } 19 | 20 | 21 | library impl { 22 | function setScriptName(string memory name) public; 23 | 24 | function setBlockNumber(uint number) public; 25 | 26 | function addWatchedBalance(ERC20Like addr) public; 27 | 28 | function advanceBlocks(uint number) public; 29 | 30 | function advanceTime(uint secs) public; 31 | } 32 | 33 | library fmt { 34 | function sprintf(string memory format, bytes memory args) public returns (string memory); 35 | 36 | function printf(string memory format, bytes memory args) public; 37 | 38 | function print(string memory message) public; 39 | 40 | function println(string memory message) public; 41 | 42 | function println() public; 43 | } 44 | 45 | library sys { 46 | function setName(string memory name) public; 47 | } 48 | 49 | library step { 50 | function run(string memory name, address who, bytes4 sel) public returns (uint); 51 | 52 | function withCaller(uint, address caller) public returns (uint); 53 | 54 | function withBalance(uint, uint balance) public returns (uint); 55 | } 56 | 57 | contract script { 58 | using step for uint; 59 | 60 | function name(string memory n) internal { 61 | impl.setScriptName(n); 62 | } 63 | 64 | function blockNumber(uint blockNr) internal { 65 | impl.setBlockNumber(blockNr); 66 | } 67 | 68 | function watchBalance(ERC20Like addr) internal { 69 | impl.addWatchedBalance(addr); 70 | } 71 | 72 | function advanceBlocks(uint blocks) internal { 73 | impl.advanceBlocks(blocks); 74 | } 75 | 76 | function advanceTime(uint secs) internal { 77 | impl.advanceTime(secs); 78 | } 79 | 80 | function run(function() external func) internal returns (uint) { 81 | return run("", func); 82 | } 83 | 84 | function run(string memory desc, function() external func) internal returns (uint) { 85 | return step.run(desc, address(func), func.selector); 86 | } 87 | 88 | // @notice configure script parameters 89 | function setup() public {} 90 | 91 | // @notice the entry point, to be implemented by you 92 | function run() public; 93 | 94 | function() external payable {} 95 | } 96 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | // const HDWalletProvider = require('@truffle/hdwallet-provider'); 22 | // const infuraKey = "fj4jll3k....."; 23 | // 24 | // const fs = require('fs'); 25 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 26 | 27 | module.exports = { 28 | /** 29 | * Networks define how you connect to your ethereum client and let you set the 30 | * defaults web3 uses to send transactions. If you don't specify one truffle 31 | * will spin up a development blockchain for you on port 9545 when you 32 | * run `develop` or `test`. You can ask a truffle command to use a specific 33 | * network from the command line, e.g 34 | * 35 | * $ truffle test --network 36 | */ 37 | 38 | networks: { 39 | // Useful for testing. The `development` name is special - truffle uses it by default 40 | // if it's defined here and no other network is specified at the command line. 41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 42 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 43 | // options below to some value. 44 | // 45 | // development: { 46 | // host: "127.0.0.1", // Localhost (default: none) 47 | // port: 8545, // Standard Ethereum port (default: none) 48 | // network_id: "*", // Any network (default: none) 49 | // }, 50 | // Another network with more advanced options... 51 | // advanced: { 52 | // port: 8777, // Custom port 53 | // network_id: 1342, // Custom network 54 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 55 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 56 | // from:
, // Account to send txs from (default: accounts[0]) 57 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 58 | // }, 59 | // Useful for deploying to a public network. 60 | // NB: It's important to wrap the provider as a function. 61 | // ropsten: { 62 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), 63 | // network_id: 3, // Ropsten's id 64 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 65 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 66 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 67 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 68 | // }, 69 | // Useful for private networks 70 | // private: { 71 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 72 | // network_id: 2111, // This network is yours, in the cloud. 73 | // production: true // Treats this network as if it was a public net. (default: false) 74 | // } 75 | }, 76 | 77 | // Set default mocha options here, use special reporters etc. 78 | mocha: { 79 | // timeout: 100000 80 | }, 81 | 82 | // Configure your compilers 83 | compilers: { 84 | solc: { 85 | // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) 86 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 87 | // settings: { // See the solidity docs for advice about optimization and evmVersion 88 | // optimizer: { 89 | // enabled: false, 90 | // runs: 200 91 | // }, 92 | // evmVersion: "byzantium" 93 | // } 94 | }, 95 | }, 96 | }; 97 | --------------------------------------------------------------------------------