├── deployments ├── mainnet │ └── .chainId └── rinkeby │ └── .chainId ├── docs ├── pages │ └── archer-core │ │ ├── lib │ │ ├── BytesLib.md │ │ ├── Context.md │ │ ├── CalldataEditor.md │ │ ├── Address.md │ │ ├── SafeERC20.md │ │ ├── SafeMath.md │ │ ├── EnumerableSet.md │ │ ├── ReentrancyGuard.md │ │ ├── Trader.md │ │ └── AccessControl.md │ │ ├── interfaces │ │ ├── IQueryEngine.md │ │ └── IERC20.md │ │ ├── QueryEngine.md │ │ ├── DispatcherFactory.md │ │ └── Dispatcher.md ├── README.md ├── SUMMARY.md └── templates │ └── contract.hbs ├── .gitbook.yaml ├── abis ├── Proxy.json ├── ERC165.json ├── IERC165.json ├── ERC165Upgradeable.json ├── IERC165Upgradeable.json ├── ERC1967Proxy.json ├── IVotingPower.json ├── QueryEngine.json ├── IQueryEngine.json ├── IAccessControl.json ├── IAccessControlUpgradeable.json ├── ITipJar.json ├── TipJarProxy.json ├── IRewardsManager.json ├── AccessControlUpgradeable.json ├── IERC20.json └── AccessControl.json ├── contracts ├── interfaces │ ├── IVotingPower.sol │ ├── IQueryEngine.sol │ ├── ITipJar.sol │ ├── IRewardsManager.sol │ ├── IERC165.sol │ ├── IERC165Upgradeable.sol │ ├── IERC20.sol │ ├── IArchERC20.sol │ ├── IDispatcherFactory.sol │ ├── ITimelockController.sol │ └── IDispatcher.sol ├── lib │ ├── 0.8 │ │ ├── Context.sol │ │ ├── ERC165.sol │ │ ├── ContextUpgradeable.sol │ │ ├── ERC165Upgradeable.sol │ │ ├── Initializable.sol │ │ ├── CheckAndSend.sol │ │ ├── ERC1967Proxy.sol │ │ ├── Proxy.sol │ │ ├── AddressUpgradeable.sol │ │ ├── AccessControl.sol │ │ ├── Address.sol │ │ └── AccessControlUpgradeable.sol │ ├── Context.sol │ ├── ReentrancyGuard.sol │ ├── CalldataEditor.sol │ ├── SafeERC20.sol │ ├── SafeMath.sol │ ├── AccessControl.sol │ └── Address.sol ├── QueryEngine.sol ├── TipJarProxy.sol └── DispatcherFactory.sol ├── .gitignore ├── .env.sample ├── arguments.js ├── deploy ├── 4_tipjar.js ├── 1_query_engine.js ├── 5_tipjar_manager.js ├── 6_timelock_controller.js ├── 2_dispatcher_factory.js ├── 8_tipjar_manager_init.js ├── 7_tipjar_proxy.js └── 3_bouncer.js ├── README.md ├── test ├── fixtures.js └── spec │ └── Bouncer.spec.js ├── package.json ├── hardhat.config.js ├── docify.js └── scripts └── createDispatcher.js /deployments/mainnet/.chainId: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /deployments/rinkeby/.chainId: -------------------------------------------------------------------------------- 1 | 4 -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/BytesLib.md: -------------------------------------------------------------------------------- 1 | ## `BytesLib` 2 | 3 | # Functions: 4 | -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/Context.md: -------------------------------------------------------------------------------- 1 | ## `Context` 2 | 3 | # Functions: 4 | -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/CalldataEditor.md: -------------------------------------------------------------------------------- 1 | ## `CalldataEditor` 2 | 3 | # Functions: 4 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./ 2 | structure: 3 | readme: ./docs/README.md 4 | summary: ./docs/SUMMARY.md 5 | -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/Address.md: -------------------------------------------------------------------------------- 1 | ## `Address` 2 | 3 | Collection of functions related to the address type 4 | 5 | # Functions: 6 | -------------------------------------------------------------------------------- /abis/Proxy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "stateMutability": "payable", 4 | "type": "fallback" 5 | }, 6 | { 7 | "stateMutability": "payable", 8 | "type": "receive" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Archer Core Contracts 2 | 3 | Archer Core is a series of smarts contracts that allow the atomic execution of arbitrage and other opportunities, while ensuring the profitability of these transactions. -------------------------------------------------------------------------------- /contracts/interfaces/IVotingPower.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | interface IVotingPower { 6 | function balanceOf(address account) external view returns (uint256); 7 | function balanceOfAt(address account, uint256 blockNumber) external view returns (uint256); 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | bin/ 3 | artifacts/ 4 | cache/ 5 | flattened/ 6 | node_modules/ 7 | deployments/localhost/ 8 | deployments/hardhat/ 9 | deployments/*/solcInputs/ 10 | *.pendingTransactions 11 | tenderly.yaml 12 | .env* 13 | !.env.sample 14 | yarn.lock 15 | yarn-error.log 16 | package.lock 17 | package-lock.json 18 | npm-debug.log 19 | *.DS_Store 20 | .vscode 21 | *.iml 22 | -------------------------------------------------------------------------------- /abis/ERC165.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes4", 6 | "name": "interfaceId", 7 | "type": "bytes4" 8 | } 9 | ], 10 | "name": "supportsInterface", 11 | "outputs": [ 12 | { 13 | "internalType": "bool", 14 | "name": "", 15 | "type": "bool" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /abis/IERC165.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes4", 6 | "name": "interfaceId", 7 | "type": "bytes4" 8 | } 9 | ], 10 | "name": "supportsInterface", 11 | "outputs": [ 12 | { 13 | "internalType": "bool", 14 | "name": "", 15 | "type": "bool" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /abis/ERC165Upgradeable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes4", 6 | "name": "interfaceId", 7 | "type": "bytes4" 8 | } 9 | ], 10 | "name": "supportsInterface", 11 | "outputs": [ 12 | { 13 | "internalType": "bool", 14 | "name": "", 15 | "type": "bool" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /abis/IERC165Upgradeable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes4", 6 | "name": "interfaceId", 7 | "type": "bytes4" 8 | } 9 | ], 10 | "name": "supportsInterface", 11 | "outputs": [ 12 | { 13 | "internalType": "bool", 14 | "name": "", 15 | "type": "bool" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /contracts/interfaces/IQueryEngine.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | interface IQueryEngine { 5 | function getPrice(address contractAddress, bytes memory data) external view returns (bytes memory); 6 | function queryAllPrices(bytes memory script, uint256[] memory inputLocations) external view returns (bytes memory); 7 | function query(bytes memory script, uint256[] memory inputLocations) external view returns (uint256); 8 | } -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/SafeERC20.md: -------------------------------------------------------------------------------- 1 | ## `SafeERC20` 2 | 3 | Wrappers around ERC20 operations that throw on failure (when the token 4 | 5 | contract returns false). Tokens that return no value (and instead revert or 6 | 7 | throw on failure) are also supported, non-reverting calls are assumed to be 8 | 9 | successful. 10 | 11 | To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, 12 | 13 | which allows you to call the safe operations as `token.safeTransfer(...)`, etc. 14 | 15 | # Functions: 16 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | export HARDHAT_NETWORK="hardhat" 2 | export INFURA_KEY="" 3 | export ETHERSCAN_API_KEY="" 4 | export CMC_API_KEY="" 5 | export REPORT_GAS="false" 6 | export DEPLOYER_ADDRESS="" 7 | export DEPLOYER_PRIVATE_KEY="" 8 | export DISPATCHER_FACTORY_ADMIN_ADDRESS="" 9 | export DISPATCHER_FACTORY_ADMIN_PRIVATE_KEY="" 10 | export DISPATCHER_FACTORY_ROLE_ADMIN_ADDRESS="" 11 | export ROLE_MANAGER_ADDRESS="" 12 | export LP_MANAGER_ADDRESS="" 13 | export WITHDRAWER_ADDRESS="" 14 | export TRADER_ADDRESS="" 15 | export SUPPLIER_ADDRESS="" 16 | export INITIAL_MAX_LIQUIDITY="" 17 | -------------------------------------------------------------------------------- /contracts/interfaces/ITipJar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface ITipJar { 5 | function tip() external payable; 6 | function updateMinerSplit(address minerAddress, address splitTo, uint32 splitPct) external; 7 | function setFeeCollector(address newCollector) external; 8 | function setFee(uint32 newFee) external; 9 | function changeAdmin(address newAdmin) external; 10 | function upgradeTo(address newImplementation) external; 11 | function upgradeToAndCall(address newImplementation, bytes calldata data) external payable; 12 | } -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/SafeMath.md: -------------------------------------------------------------------------------- 1 | ## `SafeMath` 2 | 3 | Wrappers over Solidity's arithmetic operations with added overflow 4 | 5 | checks. 6 | 7 | Arithmetic operations in Solidity wrap on overflow. This can easily result 8 | 9 | in bugs, because programmers usually assume that an overflow raises an 10 | 11 | error, which is the standard behavior in high level programming languages. 12 | 13 | `SafeMath` restores this intuition by reverting the transaction when an 14 | 15 | operation overflows. 16 | 17 | Using this library instead of the unchecked operations eliminates an entire 18 | 19 | class of bugs, so it's recommended to use it always. 20 | 21 | # Functions: 22 | -------------------------------------------------------------------------------- /arguments.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 2, 3 | "0xb2e434c789557a3A4eF9E6085D1c6F11096C3927", // queryEngine address 4 | "0x13d5B8Fc84F73fc5a0A5832Aa8373044371314d3", // roleManager address 5 | "0xa52422BB8c29E4d55243d310fB6BAe793162452e", // lpManager address 6 | "0x04EcEB77965BB426C54EE70d7fcEB2a9bDBdAfed", // withdrawer address 7 | "0xFd90411B0c246743aE0000BB18c723A3BB909Dee", // trader address 8 | "0x4F8f512Dab59F227EA70B1D8A0044aFa95CC80C3", // supplier address 9 | "100000000000000000000", // initialMaxLiquidity 10 | [ 11 | "0x4F8f512Dab59F227EA70B1D8A0044aFa95CC80C3", 12 | "0xdA69FD338DbdA8F1b96E367f33d8259c7c260d07" 13 | ] // lpWhitelist 14 | ]; 15 | -------------------------------------------------------------------------------- /deploy/4_tipjar.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ getNamedAccounts, deployments }) => { 2 | const { deploy, log } = deployments; 3 | const { deployer } = await getNamedAccounts(); 4 | 5 | log(`4) TipJar`) 6 | // Deploy TipJar contract 7 | const deployResult = await deploy("TipJar", { 8 | from: deployer, 9 | contract: "TipJar", 10 | gas: 4000000, 11 | skipIfAlreadyDeployed: false 12 | }); 13 | 14 | if (deployResult.newlyDeployed) { 15 | log(`- ${deployResult.contractName} deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`); 16 | } else { 17 | log(`- Deployment skipped, using previous deployment at: ${deployResult.address}`) 18 | } 19 | }; 20 | 21 | module.exports.tags = ["4", "TipJar"] 22 | module.exports.dependencies = ["3"] -------------------------------------------------------------------------------- /deploy/1_query_engine.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ getNamedAccounts, deployments }) => { 2 | const { deploy, log } = deployments; 3 | const { deployer } = await getNamedAccounts(); 4 | 5 | log(`1) Query Engine`) 6 | // Deploy QueryEngine contract 7 | const deployResult = await deploy("QueryEngine", { 8 | from: deployer, 9 | contract: "QueryEngine", 10 | gas: 4000000, 11 | args: [], 12 | skipIfAlreadyDeployed: true 13 | }); 14 | 15 | if (deployResult.newlyDeployed) { 16 | log(`- ${deployResult.contractName} deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`); 17 | } else { 18 | log(`- Deployment skipped, using previous deployment at: ${deployResult.address}`) 19 | } 20 | }; 21 | 22 | module.exports.tags = ["1", "QueryEngine"] -------------------------------------------------------------------------------- /contracts/interfaces/IRewardsManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | import "./IERC20.sol"; 5 | 6 | interface IRewardsManager { 7 | function deposit(uint256 pid, uint256 amount) external; 8 | function depositWithPermit(uint256 pid, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; 9 | function withdraw(uint256 pid, uint256 amount) external; 10 | function emergencyWithdraw(uint256 pid) external; 11 | function rewardToken() external view returns (IERC20); 12 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 13 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 14 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 15 | 16 | } -------------------------------------------------------------------------------- /deploy/5_tipjar_manager.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ getNamedAccounts, deployments }) => { 2 | const { deploy, log } = deployments; 3 | const { deployer } = await getNamedAccounts(); 4 | 5 | log(`5) TipJarManager`) 6 | // Deploy TipJarManager contract 7 | const deployResult = await deploy("TipJarManager", { 8 | from: deployer, 9 | contract: "TipJarManager", 10 | gas: 4000000, 11 | skipIfAlreadyDeployed: true 12 | }); 13 | 14 | if (deployResult.newlyDeployed) { 15 | log(`- ${deployResult.contractName} deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`); 16 | } else { 17 | log(`- Deployment skipped, using previous deployment at: ${deployResult.address}`) 18 | } 19 | }; 20 | 21 | module.exports.tags = ["5", "TipJarManager"] 22 | module.exports.dependencies = ["4"] -------------------------------------------------------------------------------- /docs/pages/archer-core/interfaces/IQueryEngine.md: -------------------------------------------------------------------------------- 1 | ## `IQueryEngine` 2 | 3 | # Functions: 4 | 5 | - [`getPrice(address contractAddress, bytes data)`](#IQueryEngine-getPrice-address-bytes-) 6 | 7 | - [`queryAllPrices(bytes script, uint256[] inputLocations)`](#IQueryEngine-queryAllPrices-bytes-uint256---) 8 | 9 | - [`query(bytes script, uint256[] inputLocations)`](#IQueryEngine-query-bytes-uint256---) 10 | 11 | # Function `getPrice(address contractAddress, bytes data) → bytes` {#IQueryEngine-getPrice-address-bytes-} 12 | 13 | No description 14 | 15 | # Function `queryAllPrices(bytes script, uint256[] inputLocations) → bytes` {#IQueryEngine-queryAllPrices-bytes-uint256---} 16 | 17 | No description 18 | 19 | # Function `query(bytes script, uint256[] inputLocations) → uint256` {#IQueryEngine-query-bytes-uint256---} 20 | 21 | No description 22 | -------------------------------------------------------------------------------- /abis/ERC1967Proxy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_logic", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "bytes", 11 | "name": "_data", 12 | "type": "bytes" 13 | } 14 | ], 15 | "stateMutability": "payable", 16 | "type": "constructor" 17 | }, 18 | { 19 | "anonymous": false, 20 | "inputs": [ 21 | { 22 | "indexed": true, 23 | "internalType": "address", 24 | "name": "implementation", 25 | "type": "address" 26 | } 27 | ], 28 | "name": "Upgraded", 29 | "type": "event" 30 | }, 31 | { 32 | "stateMutability": "payable", 33 | "type": "fallback" 34 | }, 35 | { 36 | "stateMutability": "payable", 37 | "type": "receive" 38 | } 39 | ] 40 | -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/EnumerableSet.md: -------------------------------------------------------------------------------- 1 | ## `EnumerableSet` 2 | 3 | Library for managing 4 | 5 | https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive 6 | 7 | types. 8 | 9 | Sets have the following properties: 10 | 11 | - Elements are added, removed, and checked for existence in constant time 12 | 13 | (O(1)). 14 | 15 | - Elements are enumerated in O(n). No guarantees are made on the ordering. 16 | 17 | ``` 18 | 19 | contract Example { 20 | 21 | // Add the library methods 22 | 23 | using EnumerableSet for EnumerableSet.AddressSet; 24 | 25 | // Declare a set state variable 26 | 27 | EnumerableSet.AddressSet private mySet; 28 | 29 | } 30 | 31 | ``` 32 | 33 | As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) 34 | 35 | and `uint256` (`UintSet`) are supported. 36 | 37 | # Functions: 38 | -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/ReentrancyGuard.md: -------------------------------------------------------------------------------- 1 | ## `ReentrancyGuard` 2 | 3 | Contract module that helps prevent reentrant calls to a function. 4 | 5 | Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier 6 | 7 | available, which can be applied to functions to make sure there are no nested 8 | 9 | (reentrant) calls to them. 10 | 11 | Note that because there is a single `nonReentrant` guard, functions marked as 12 | 13 | `nonReentrant` may not call one another. This can be worked around by making 14 | 15 | those functions `private`, and then adding `external` `nonReentrant` entry 16 | 17 | points to them. 18 | 19 | TIP: If you would like to learn more about reentrancy and alternative ways 20 | 21 | to protect against it, check out our blog post 22 | 23 | https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. 24 | 25 | # Functions: 26 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC165 standard, as defined in the 7 | * https://eips.ethereum.org/EIPS/eip-165[EIP]. 8 | * 9 | * Implementers can declare support of contract interfaces, which can then be 10 | * queried by others ({ERC165Checker}). 11 | * 12 | * For an implementation, see {ERC165}. 13 | */ 14 | interface IERC165 { 15 | /** 16 | * @dev Returns true if this contract implements the interface defined by 17 | * `interfaceId`. See the corresponding 18 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 19 | * to learn more about how these ids are created. 20 | * 21 | * This function call must use less than 30 000 gas. 22 | */ 23 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 24 | } 25 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC165Upgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC165 standard, as defined in the 7 | * https://eips.ethereum.org/EIPS/eip-165[EIP]. 8 | * 9 | * Implementers can declare support of contract interfaces, which can then be 10 | * queried by others ({ERC165Checker}). 11 | * 12 | * For an implementation, see {ERC165}. 13 | */ 14 | interface IERC165Upgradeable { 15 | /** 16 | * @dev Returns true if this contract implements the interface defined by 17 | * `interfaceId`. See the corresponding 18 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 19 | * to learn more about how these ids are created. 20 | * 21 | * This function call must use less than 30 000 gas. 22 | */ 23 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 24 | } 25 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | interface IERC20 { 5 | function name() external view returns (string memory); 6 | function symbol() external view returns (string memory); 7 | function decimals() external view returns (uint8); 8 | function totalSupply() external view returns (uint256); 9 | function balanceOf(address account) external view returns (uint256); 10 | function transfer(address recipient, uint256 amount) external returns (bool); 11 | function allowance(address owner, address spender) external view returns (uint256); 12 | function approve(address spender, uint256 amount) external returns (bool); 13 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 14 | event Transfer(address indexed from, address indexed to, uint256 value); 15 | event Approval(address indexed owner, address indexed spender, uint256 value); 16 | } -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | * Archer Core 3 | * [Dispatcher](pages/archer-core/Dispatcher.md) 4 | * [DispatcherFactory](pages/archer-core/DispatcherFactory.md) 5 | * [QueryEngine](pages/archer-core/QueryEngine.md) 6 | * Interfaces 7 | * [IERC20](pages/archer-core/interfaces/IERC20.md) 8 | * [IQueryEngine](pages/archer-core/interfaces/IQueryEngine.md) 9 | * Lib 10 | * [AccessControl](pages/archer-core/lib/AccessControl.md) 11 | * [Address](pages/archer-core/lib/Address.md) 12 | * [BytesLib](pages/archer-core/lib/BytesLib.md) 13 | * [CalldataEditor](pages/archer-core/lib/CalldataEditor.md) 14 | * [Context](pages/archer-core/lib/Context.md) 15 | * [EnumerableSet](pages/archer-core/lib/EnumerableSet.md) 16 | * [ReentrancyGuard](pages/archer-core/lib/ReentrancyGuard.md) 17 | * [SafeERC20](pages/archer-core/lib/SafeERC20.md) 18 | * [SafeMath](pages/archer-core/lib/SafeMath.md) 19 | * [Trader](pages/archer-core/lib/Trader.md) 20 | -------------------------------------------------------------------------------- /abis/IVotingPower.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "account", 7 | "type": "address" 8 | } 9 | ], 10 | "name": "balanceOf", 11 | "outputs": [ 12 | { 13 | "internalType": "uint256", 14 | "name": "", 15 | "type": "uint256" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | }, 21 | { 22 | "inputs": [ 23 | { 24 | "internalType": "address", 25 | "name": "account", 26 | "type": "address" 27 | }, 28 | { 29 | "internalType": "uint256", 30 | "name": "blockNumber", 31 | "type": "uint256" 32 | } 33 | ], 34 | "name": "balanceOfAt", 35 | "outputs": [ 36 | { 37 | "internalType": "uint256", 38 | "name": "", 39 | "type": "uint256" 40 | } 41 | ], 42 | "stateMutability": "view", 43 | "type": "function" 44 | } 45 | ] 46 | -------------------------------------------------------------------------------- /contracts/lib/0.8/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /* 6 | * @dev Provides information about the current execution context, including the 7 | * sender of the transaction and its data. While these are generally available 8 | * via msg.sender and msg.data, they should not be accessed in such a direct 9 | * manner, since when dealing with meta-transactions the account sending and 10 | * paying for execution may not be the actual sender (as far as an application 11 | * is concerned). 12 | * 13 | * This contract is only required for intermediate, library-like contracts. 14 | */ 15 | abstract contract Context { 16 | function _msgSender() internal view virtual returns (address) { 17 | return msg.sender; 18 | } 19 | 20 | function _msgData() internal view virtual returns (bytes calldata) { 21 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 22 | return msg.data; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/lib/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.0 <0.8.0; 4 | 5 | /* 6 | * @dev Provides information about the current execution context, including the 7 | * sender of the transaction and its data. While these are generally available 8 | * via msg.sender and msg.data, they should not be accessed in such a direct 9 | * manner, since when dealing with GSN meta-transactions the account sending and 10 | * paying for execution may not be the actual sender (as far as an application 11 | * is concerned). 12 | * 13 | * This contract is only required for intermediate, library-like contracts. 14 | */ 15 | abstract contract Context { 16 | function _msgSender() internal view virtual returns (address payable) { 17 | return msg.sender; 18 | } 19 | 20 | function _msgData() internal view virtual returns (bytes memory) { 21 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 22 | return msg.data; 23 | } 24 | } -------------------------------------------------------------------------------- /contracts/lib/0.8/ERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "../../interfaces/IERC165.sol"; 6 | 7 | /** 8 | * @dev Implementation of the {IERC165} interface. 9 | * 10 | * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check 11 | * for the additional interface id that will be supported. For example: 12 | * 13 | * ```solidity 14 | * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 15 | * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); 16 | * } 17 | * ``` 18 | * 19 | * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. 20 | */ 21 | abstract contract ERC165 is IERC165 { 22 | /** 23 | * @dev See {IERC165-supportsInterface}. 24 | */ 25 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 26 | return interfaceId == type(IERC165).interfaceId; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /deploy/6_timelock_controller.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ getNamedAccounts, deployments }) => { 2 | const { deploy, log } = deployments; 3 | const { deployer } = await getNamedAccounts(); 4 | const TIMELOCK_MIN_DELAY = process.env.TIMELOCK_MIN_DELAY 5 | const tipJarManager = await deployments.get("TipJarManager") 6 | 7 | log(`6) TimelockController`) 8 | // Deploy TimelockController contract 9 | const deployResult = await deploy("TimelockController", { 10 | from: deployer, 11 | contract: "TimelockController", 12 | gas: 4000000, 13 | args: [TIMELOCK_MIN_DELAY, [tipJarManager.address], [tipJarManager.address]], 14 | skipIfAlreadyDeployed: true 15 | }); 16 | 17 | if (deployResult.newlyDeployed) { 18 | log(`- ${deployResult.contractName} deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`); 19 | } else { 20 | log(`- Deployment skipped, using previous deployment at: ${deployResult.address}`) 21 | } 22 | }; 23 | 24 | module.exports.tags = ["6", "TimelockController"] 25 | module.exports.dependencies = ["5"] -------------------------------------------------------------------------------- /deploy/2_dispatcher_factory.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ getNamedAccounts, deployments }) => { 2 | const { deploy, log } = deployments; 3 | const { deployer } = await getNamedAccounts(); 4 | const DISPATCHER_FACTORY_ADMIN_ADDRESS = process.env.DISPATCHER_FACTORY_ADMIN_ADDRESS 5 | const DISPATCHER_FACTORY_ROLE_ADMIN_ADDRESS = process.env.DISPATCHER_FACTORY_ROLE_ADMIN_ADDRESS 6 | 7 | log(`2) Dispatcher Factory`) 8 | // Deploy Dispatcher Factory contract 9 | const deployResult = await deploy("DispatcherFactory", { 10 | from: deployer, 11 | contract: "DispatcherFactory", 12 | gas: 4000000, 13 | args: [DISPATCHER_FACTORY_ROLE_ADMIN_ADDRESS, DISPATCHER_FACTORY_ADMIN_ADDRESS], 14 | skipIfAlreadyDeployed: true 15 | }); 16 | 17 | if (deployResult.newlyDeployed) { 18 | log(`- ${deployResult.contractName} deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`); 19 | } else { 20 | log(`- Deployment skipped, using previous deployment at: ${deployResult.address}`) 21 | } 22 | }; 23 | 24 | module.exports.tags = ["2", "DispatcherFactory"] 25 | module.exports.dependencies = ["1"] -------------------------------------------------------------------------------- /deploy/8_tipjar_manager_init.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ ethers, getNamedAccounts, deployments }) => { 2 | const { execute, log } = deployments; 3 | const { deployer, admin } = await getNamedAccounts(); 4 | const tipJarProxy = await deployments.get("TipJarProxy") 5 | const timelockController = await deployments.get("TimelockController") 6 | const TIMELOCK_CRITICAL_DELAY = process.env.TIMELOCK_CRITICAL_DELAY 7 | const TIMELOCK_REGULAR_DELAY = process.env.TIMELOCK_REGULAR_DELAY 8 | 9 | log(`8) TipJarManager Init`) 10 | // Initialize TipJarManager contract 11 | await execute('TipJarManager', {from: deployer }, 'initialize', tipJarProxy.address, admin, timelockController.address, TIMELOCK_CRITICAL_DELAY, TIMELOCK_REGULAR_DELAY); 12 | log(`- TipJar Manager initialized`) 13 | }; 14 | 15 | module.exports.skip = async function({ deployments }) { 16 | const { read, log } = deployments 17 | const tipJar = await read('TipJarManager', 'tipJar'); 18 | const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" 19 | if(tipJar == ZERO_ADDRESS) { 20 | return false 21 | } 22 | log(`8) TipJarManager Init`) 23 | log(`- Skipping step, TipJar Manager already initialized`) 24 | return true 25 | } 26 | 27 | module.exports.tags = ["8", "TipJarManagerInit"] 28 | module.exports.dependencies = ["7"] -------------------------------------------------------------------------------- /contracts/lib/0.8/ContextUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | import "./Initializable.sol"; 5 | 6 | /* 7 | * @dev Provides information about the current execution context, including the 8 | * sender of the transaction and its data. While these are generally available 9 | * via msg.sender and msg.data, they should not be accessed in such a direct 10 | * manner, since when dealing with meta-transactions the account sending and 11 | * paying for execution may not be the actual sender (as far as an application 12 | * is concerned). 13 | * 14 | * This contract is only required for intermediate, library-like contracts. 15 | */ 16 | abstract contract ContextUpgradeable is Initializable { 17 | function __Context_init() internal initializer { 18 | __Context_init_unchained(); 19 | } 20 | 21 | function __Context_init_unchained() internal initializer { 22 | } 23 | function _msgSender() internal view virtual returns (address) { 24 | return msg.sender; 25 | } 26 | 27 | function _msgData() internal view virtual returns (bytes calldata) { 28 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 29 | return msg.data; 30 | } 31 | uint256[50] private __gap; 32 | } 33 | -------------------------------------------------------------------------------- /contracts/lib/0.8/ERC165Upgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "../../interfaces/IERC165Upgradeable.sol"; 6 | import "./Initializable.sol"; 7 | 8 | /** 9 | * @dev Implementation of the {IERC165} interface. 10 | * 11 | * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check 12 | * for the additional interface id that will be supported. For example: 13 | * 14 | * ```solidity 15 | * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 16 | * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); 17 | * } 18 | * ``` 19 | * 20 | * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. 21 | */ 22 | abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable { 23 | function __ERC165_init() internal initializer { 24 | __ERC165_init_unchained(); 25 | } 26 | 27 | function __ERC165_init_unchained() internal initializer { 28 | } 29 | /** 30 | * @dev See {IERC165-supportsInterface}. 31 | */ 32 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 33 | return interfaceId == type(IERC165Upgradeable).interfaceId; 34 | } 35 | uint256[50] private __gap; 36 | } -------------------------------------------------------------------------------- /docs/templates/contract.hbs: -------------------------------------------------------------------------------- 1 | ## `{{name}}` 2 | 3 | {{{natspec.userdoc}}} 4 | 5 | {{{natspec.devdoc}}} 6 | 7 | {{#if ownFunctions}} 8 | # Functions: 9 | {{#ownFunctions}} 10 | {{#if (or (eq visibility "public") (eq visibility "external"))}} 11 | - [`{{name}}({{args}})`](#{{anchor}}) 12 | {{/if}} 13 | {{/ownFunctions}} 14 | {{/if}} 15 | 16 | {{#if ownEvents}} 17 | # Events: 18 | {{#ownEvents}} 19 | - [`{{name}}({{args}})`](#{{anchor}}) 20 | {{/ownEvents}} 21 | {{/if}} 22 | 23 | {{#ownFunctions}} 24 | {{#if (or (eq visibility "public") (eq visibility "external"))}} 25 | # Function `{{name}}({{args}}){{#if outputs}} → {{outputs}}{{/if}}` {#{{anchor~}} } 26 | {{#if natspec.userdoc}}{{natspec.userdoc}}{{else}}No description{{/if}} 27 | {{{natspec.devdoc}}} 28 | {{#if natspec.params}} 29 | ## Parameters: 30 | {{#natspec.params}} 31 | - `{{param}}`: {{description}} 32 | {{/natspec.params}} 33 | {{/if}} 34 | {{#if natspec.returns}} 35 | ## Return Values: 36 | {{#natspec.returns}} 37 | - {{param}} {{description}} 38 | {{/natspec.returns}} 39 | {{/if}} 40 | {{/if}} 41 | {{/ownFunctions}} 42 | 43 | {{#ownEvents}} 44 | # Event `{{name}}({{args}})` {#{{anchor~}} } 45 | {{#if natspec.userdoc}}{{natspec.userdoc}}{{else}}No description{{/if}} 46 | {{{natspec.devdoc}}} 47 | {{#if natspec.params}} 48 | ## Parameters: 49 | {{#natspec.params}} 50 | - `{{param}}`: {{description}} 51 | {{/natspec.params}} 52 | {{/if}} 53 | {{/ownEvents}} -------------------------------------------------------------------------------- /deploy/7_tipjar_proxy.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ ethers, getNamedAccounts, deployments }) => { 2 | const { deploy, log } = deployments; 3 | const { deployer } = await getNamedAccounts(); 4 | const tipJar = await deployments.get("TipJar") 5 | const timelockController = await deployments.get("TimelockController") 6 | const TIP_JAR_FEE_COLLECTOR = process.env.TIP_JAR_FEE_COLLECTOR 7 | const TIP_JAR_FEE = process.env.TIP_JAR_FEE 8 | const tipJarManager = await deployments.get("TipJarManager") 9 | const tipJarInterface = new ethers.utils.Interface(tipJar.abi); 10 | const initData = tipJarInterface.encodeFunctionData("initialize", [ 11 | timelockController.address, 12 | timelockController.address, 13 | TIP_JAR_FEE_COLLECTOR, 14 | TIP_JAR_FEE 15 | ] 16 | ); 17 | 18 | log(`7) TipJarProxy`) 19 | // Deploy TipJarProxy contract 20 | const deployResult = await deploy("TipJarProxy", { 21 | from: deployer, 22 | contract: "TipJarProxy", 23 | gas: 4000000, 24 | args: [tipJar.address, tipJarManager.address, initData], 25 | skipIfAlreadyDeployed: true 26 | }); 27 | 28 | if (deployResult.newlyDeployed) { 29 | log(`- ${deployResult.contractName} deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`); 30 | } else { 31 | log(`- Deployment skipped, using previous deployment at: ${deployResult.address}`) 32 | } 33 | }; 34 | 35 | module.exports.tags = ["7", "TipJarProxy"] 36 | module.exports.dependencies = ["6"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archer Core Contracts 2 | 3 | Archer Core is a series of smarts contracts that allow for the atomic execution of arbitrage and other opportunities on the Ethereum blockchain, while ensuring the profitability of the resulting transactions. These contracts are used by the Archer network to execute any transaction sent via the Archer Rest API. 4 | 5 | ## How it Works 6 | Each supplier that signs up for the Archer network is assigned 3 things: 7 | * An API key used to submit opportunities to the network (unique to each supplier) 8 | * A Bot ID that is used to identify the bot sending each opportunity + distribute rewards to suppliers (each supplier can have multiple) 9 | * A Dispatcher contract that executes transactions sent via API request and, optionally, serves as a liquidity pool the supplier can use to support their strategies 10 | 11 | Suppliers send POST requests to the Archer REST API with the payloads necessary to execute their transactions. [See documentation for these requests here.](https://docs.google.com/document/d/178mTvHjqIM0sFx_AM3NpnqCG68WNKvtrgKc3iSMAE2g) 12 | 13 | Archer finds the most profitable transactions each block and submits them to the network on behalf of the suppliers. If a miner within the Archer network mines this transaction before the opportunity expires and places it in priority position (first tx in block), then the resulting profit will be split between the miner, the supplier, and the network (with splits and other incentives to be determined by Archer DAO). 14 | 15 | -------------------------------------------------------------------------------- /deploy/3_bouncer.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ getNamedAccounts, deployments }) => { 2 | const { deploy, log } = deployments; 3 | const { deployer } = await getNamedAccounts(); 4 | const dispatcherFactory = await deployments.get("DispatcherFactory"); 5 | const DISPATCHER_FACTORY_ADMIN_ADDRESS = process.env.DISPATCHER_FACTORY_ADMIN_ADDRESS 6 | const DISPATCHER_FACTORY_ROLE_ADMIN_ADDRESS = process.env.DISPATCHER_FACTORY_ROLE_ADMIN_ADDRESS 7 | const VOTING_POWER_PRISM_ADDRESS = process.env.VOTING_POWER_PRISM_ADDRESS 8 | const GLOBAL_MAX_CONTRIBUTION_PCT = process.env.GLOBAL_MAX_CONTRIBUTION_PCT 9 | const DISPATCHER_MAX_CONTRIBUTION_PCT = process.env.DISPATCHER_MAX_CONTRIBUTION_PCT 10 | const BANKROLL_REQUIRED_VOTING_POWER = process.env.BANKROLL_REQUIRED_VOTING_POWER 11 | 12 | // log(`3) Bouncer`) 13 | // // Deploy Bouncer contract 14 | // const deployResult = await deploy("Bouncer", { 15 | // from: deployer, 16 | // contract: "Bouncer", 17 | // gas: 4000000, 18 | // args: [dispatcherFactory.address, VOTING_POWER_PRISM_ADDRESS, GLOBAL_MAX_CONTRIBUTION_PCT, DISPATCHER_MAX_CONTRIBUTION_PCT, BANKROLL_REQUIRED_VOTING_POWER, DISPATCHER_FACTORY_ADMIN_ADDRESS, DISPATCHER_FACTORY_ROLE_ADMIN_ADDRESS], 19 | // skipIfAlreadyDeployed: true 20 | // }); 21 | 22 | // if (deployResult.newlyDeployed) { 23 | // log(`- ${deployResult.contractName} deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`); 24 | // } else { 25 | // log(`- Deployment skipped, using previous deployment at: ${deployResult.address}`) 26 | // } 27 | }; 28 | 29 | module.exports.tags = ["3", "Bouncer"] 30 | module.exports.dependencies = ["2"] -------------------------------------------------------------------------------- /abis/QueryEngine.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "contractAddress", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "bytes", 11 | "name": "data", 12 | "type": "bytes" 13 | } 14 | ], 15 | "name": "getPrice", 16 | "outputs": [ 17 | { 18 | "internalType": "bytes", 19 | "name": "", 20 | "type": "bytes" 21 | } 22 | ], 23 | "stateMutability": "view", 24 | "type": "function" 25 | }, 26 | { 27 | "inputs": [ 28 | { 29 | "internalType": "bytes", 30 | "name": "script", 31 | "type": "bytes" 32 | }, 33 | { 34 | "internalType": "uint256[]", 35 | "name": "inputLocations", 36 | "type": "uint256[]" 37 | } 38 | ], 39 | "name": "query", 40 | "outputs": [ 41 | { 42 | "internalType": "uint256", 43 | "name": "", 44 | "type": "uint256" 45 | } 46 | ], 47 | "stateMutability": "view", 48 | "type": "function" 49 | }, 50 | { 51 | "inputs": [ 52 | { 53 | "internalType": "bytes", 54 | "name": "script", 55 | "type": "bytes" 56 | }, 57 | { 58 | "internalType": "uint256[]", 59 | "name": "inputLocations", 60 | "type": "uint256[]" 61 | } 62 | ], 63 | "name": "queryAllPrices", 64 | "outputs": [ 65 | { 66 | "internalType": "bytes", 67 | "name": "", 68 | "type": "bytes" 69 | } 70 | ], 71 | "stateMutability": "view", 72 | "type": "function" 73 | } 74 | ] 75 | -------------------------------------------------------------------------------- /abis/IQueryEngine.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "contractAddress", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "bytes", 11 | "name": "data", 12 | "type": "bytes" 13 | } 14 | ], 15 | "name": "getPrice", 16 | "outputs": [ 17 | { 18 | "internalType": "bytes", 19 | "name": "", 20 | "type": "bytes" 21 | } 22 | ], 23 | "stateMutability": "view", 24 | "type": "function" 25 | }, 26 | { 27 | "inputs": [ 28 | { 29 | "internalType": "bytes", 30 | "name": "script", 31 | "type": "bytes" 32 | }, 33 | { 34 | "internalType": "uint256[]", 35 | "name": "inputLocations", 36 | "type": "uint256[]" 37 | } 38 | ], 39 | "name": "query", 40 | "outputs": [ 41 | { 42 | "internalType": "uint256", 43 | "name": "", 44 | "type": "uint256" 45 | } 46 | ], 47 | "stateMutability": "view", 48 | "type": "function" 49 | }, 50 | { 51 | "inputs": [ 52 | { 53 | "internalType": "bytes", 54 | "name": "script", 55 | "type": "bytes" 56 | }, 57 | { 58 | "internalType": "uint256[]", 59 | "name": "inputLocations", 60 | "type": "uint256[]" 61 | } 62 | ], 63 | "name": "queryAllPrices", 64 | "outputs": [ 65 | { 66 | "internalType": "bytes", 67 | "name": "", 68 | "type": "bytes" 69 | } 70 | ], 71 | "stateMutability": "view", 72 | "type": "function" 73 | } 74 | ] 75 | -------------------------------------------------------------------------------- /docs/pages/archer-core/QueryEngine.md: -------------------------------------------------------------------------------- 1 | ## `QueryEngine` 2 | 3 | # Functions: 4 | 5 | - [`getPrice(address contractAddress, bytes data)`](#QueryEngine-getPrice-address-bytes-) 6 | 7 | - [`queryAllPrices(bytes script, uint256[] inputLocations)`](#QueryEngine-queryAllPrices-bytes-uint256---) 8 | 9 | - [`query(bytes script, uint256[] inputLocations)`](#QueryEngine-query-bytes-uint256---) 10 | 11 | # Function `getPrice(address contractAddress, bytes data) → bytes` {#QueryEngine-getPrice-address-bytes-} 12 | 13 | Calls the price function specified by data at contractAddress, returning the price as bytes 14 | 15 | ## Parameters: 16 | 17 | - `contractAddress`: contract to query 18 | 19 | - `data`: the bytecode for the contract call 20 | 21 | ## Return Values: 22 | 23 | - price in bytes 24 | 25 | # Function `queryAllPrices(bytes script, uint256[] inputLocations) → bytes` {#QueryEngine-queryAllPrices-bytes-uint256---} 26 | 27 | Makes a series of queries at once, returning all the prices as bytes 28 | 29 | ## Parameters: 30 | 31 | - `script`: the compiled bytecode for the series of function calls to get the final price 32 | 33 | - `inputLocations`: index locations within the script to insert input amounts dynamically 34 | 35 | ## Return Values: 36 | 37 | - all prices as bytes 38 | 39 | # Function `query(bytes script, uint256[] inputLocations) → uint256` {#QueryEngine-query-bytes-uint256---} 40 | 41 | Makes a series of queries at once, returning the final price as a uint 42 | 43 | ## Parameters: 44 | 45 | - `script`: the compiled bytecode for the series of function calls to get the final price 46 | 47 | - `inputLocations`: index locations within the script to insert input amounts dynamically 48 | 49 | ## Return Values: 50 | 51 | - last price as uint 52 | -------------------------------------------------------------------------------- /test/fixtures.js: -------------------------------------------------------------------------------- 1 | const { deployments } = require("hardhat"); 2 | 3 | const VOTING_POWER_PRISM_ADDRESS = process.env.VOTING_POWER_PRISM_ADDRESS 4 | const GLOBAL_MAX_CONTRIBUTION_PCT = process.env.GLOBAL_MAX_CONTRIBUTION_PCT 5 | const DISPATCHER_MAX_CONTRIBUTION_PCT = process.env.DISPATCHER_MAX_CONTRIBUTION_PCT 6 | const BANKROLL_REQUIRED_VOTING_POWER = process.env.BANKROLL_REQUIRED_VOTING_POWER 7 | const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" 8 | 9 | const bankrollFixture = deployments.createFixture(async ({deployments, getNamedAccounts, getUnnamedAccounts, ethers}, options) => { 10 | const accounts = await ethers.getSigners(); 11 | const deployer = accounts[0] 12 | const admin = accounts[1] 13 | const alice = accounts[2] 14 | const bob = accounts[3] 15 | const QueryEngineFactory = await ethers.getContractFactory("QueryEngine") 16 | const QueryEngine = await QueryEngineFactory.deploy() 17 | const DispatcherFactoryFactory = await ethers.getContractFactory("DispatcherFactory") 18 | const DispatcherFactory = await DispatcherFactoryFactory.deploy(admin.address, admin.address) 19 | const BouncerFactory = await ethers.getContractFactory("Bouncer") 20 | const Bouncer = await BouncerFactory.deploy( 21 | DispatcherFactory.address, 22 | VOTING_POWER_PRISM_ADDRESS, 23 | GLOBAL_MAX_CONTRIBUTION_PCT, 24 | DISPATCHER_MAX_CONTRIBUTION_PCT, 25 | BANKROLL_REQUIRED_VOTING_POWER, 26 | admin.address, 27 | admin.address 28 | ) 29 | 30 | return { 31 | deployer: deployer, 32 | admin: admin, 33 | alice: alice, 34 | bob: bob, 35 | dispatcherFactory: DispatcherFactory, 36 | bouncer: Bouncer, 37 | queryEngine: QueryEngine, 38 | ZERO_ADDRESS: ZERO_ADDRESS 39 | }; 40 | }) 41 | 42 | module.exports.bankrollFixture = bankrollFixture; -------------------------------------------------------------------------------- /contracts/interfaces/IArchERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | interface IArchERC20 { 5 | function name() external view returns (string memory); 6 | function symbol() external view returns (string memory); 7 | function decimals() external view returns (uint8); 8 | function totalSupply() external view returns (uint256); 9 | function version() external view returns (uint8); 10 | function balanceOf(address account) external view returns (uint256); 11 | function transfer(address recipient, uint256 amount) external returns (bool); 12 | function allowance(address owner, address spender) external view returns (uint256); 13 | function approve(address spender, uint256 amount) external returns (bool); 14 | function increaseAllowance(address spender, uint256 addedValue) external returns (bool); 15 | function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); 16 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 17 | function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; 18 | function mint(address dst, uint256 amount) external returns (bool); 19 | function burn(address src, uint256 amount) external returns (bool); 20 | function supplyManager() external view returns (address); 21 | function setSupplyManager(address newSupplyManager) external returns (bool); 22 | function getDomainSeparator() external view returns (bytes32); 23 | function DOMAIN_TYPEHASH() external view returns (bytes32); 24 | function VERSION_HASH() external view returns (bytes32); 25 | function PERMIT_TYPEHASH() external view returns (bytes32); 26 | event Transfer(address indexed from, address indexed to, uint256 value); 27 | event Approval(address indexed owner, address indexed spender, uint256 value); 28 | } -------------------------------------------------------------------------------- /contracts/lib/0.8/Initializable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // solhint-disable-next-line compiler-version 4 | pragma solidity ^0.8.0; 5 | 6 | import "./AddressUpgradeable.sol"; 7 | 8 | /** 9 | * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed 10 | * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an 11 | * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer 12 | * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. 13 | * 14 | * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as 15 | * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. 16 | * 17 | * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure 18 | * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. 19 | */ 20 | abstract contract Initializable { 21 | 22 | /** 23 | * @dev Indicates that the contract has been initialized. 24 | */ 25 | bool private _initialized; 26 | 27 | /** 28 | * @dev Indicates that the contract is in the process of being initialized. 29 | */ 30 | bool private _initializing; 31 | 32 | /** 33 | * @dev Modifier to protect an initializer function from being invoked twice. 34 | */ 35 | modifier initializer() { 36 | require(_initializing || !_initialized, "Initializable: contract is already initialized"); 37 | 38 | bool isTopLevelCall = !_initializing; 39 | if (isTopLevelCall) { 40 | _initializing = true; 41 | _initialized = true; 42 | } 43 | 44 | _; 45 | 46 | if (isTopLevelCall) { 47 | _initializing = false; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /contracts/interfaces/IDispatcherFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | interface IDispatcherFactory { 5 | function version() external view returns (uint8); 6 | function dispatchers() external view returns (address[] memory); 7 | function exists(address dispatcher) external view returns (bool); 8 | function numDispatchers() external view returns (uint256); 9 | function createNewDispatcher(address queryEngine, address roleManager, address lpManager, address withdrawer, address trader, address supplier, uint256 initialMaxLiquidity, address[] memory lpWhitelist) external returns (address); 10 | function addDispatchers(address[] memory dispatcherContracts) external; 11 | function removeDispatcher(address dispatcherContract) external; 12 | function DISPATCHER_ADMIN_ROLE() external view returns (bytes32); 13 | function DEFAULT_ADMIN_ROLE() external view returns (bytes32); 14 | function hasRole(bytes32 role, address account) external view returns (bool); 15 | function getRoleMemberCount(bytes32 role) external view returns (uint256); 16 | function getRoleMember(bytes32 role, uint256 index) external view returns (address); 17 | function getRoleAdmin(bytes32 role) external view returns (bytes32); 18 | function grantRole(bytes32 role, address account) external; 19 | function revokeRole(bytes32 role, address account) external; 20 | function renounceRole(bytes32 role, address account) external; 21 | event DispatcherCreated(address indexed dispatcher, uint8 indexed version, address queryEngine, address roleManager, address lpManager, address withdrawer, address trader, address supplier, uint256 initialMaxLiquidity, bool lpWhitelist); 22 | event DispatcherAdded(address indexed dispatcher); 23 | event DispatcherRemoved(address indexed dispatcher); 24 | event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); 25 | event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 26 | event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 27 | } -------------------------------------------------------------------------------- /contracts/lib/0.8/CheckAndSend.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | /* 5 | Copyright 2021 Flashbots: Scott Bigelow (scott@flashbots.net). 6 | */ 7 | 8 | contract CheckAndSend { 9 | function _check32BytesMulti( 10 | address[] calldata _targets, 11 | bytes[] calldata _payloads, 12 | bytes32[] calldata _resultMatches 13 | ) internal view { 14 | require(_targets.length == _payloads.length); 15 | require(_targets.length == _resultMatches.length); 16 | for (uint256 i = 0; i < _targets.length; i++) { 17 | _check32Bytes(_targets[i], _payloads[i], _resultMatches[i]); 18 | } 19 | } 20 | 21 | function _checkBytesMulti( 22 | address[] calldata _targets, 23 | bytes[] calldata _payloads, 24 | bytes[] calldata _resultMatches 25 | ) internal view { 26 | require(_targets.length == _payloads.length); 27 | require(_targets.length == _resultMatches.length); 28 | for (uint256 i = 0; i < _targets.length; i++) { 29 | _checkBytes(_targets[i], _payloads[i], _resultMatches[i]); 30 | } 31 | } 32 | 33 | function _check32Bytes( 34 | address _target, 35 | bytes memory _payload, 36 | bytes32 _resultMatch 37 | ) internal view { 38 | (bool _success, bytes memory _response) = _target.staticcall(_payload); 39 | require(_success, "!success"); 40 | require(_response.length >= 32, "response less than 32 bytes"); 41 | bytes32 _responseScalar; 42 | assembly { 43 | _responseScalar := mload(add(_response, 0x20)) 44 | } 45 | require(_responseScalar == _resultMatch, "response mismatch"); 46 | } 47 | 48 | function _checkBytes( 49 | address _target, 50 | bytes memory _payload, 51 | bytes memory _resultMatch 52 | ) internal view { 53 | (bool _success, bytes memory _response) = _target.staticcall(_payload); 54 | require(_success, "!success"); 55 | require( 56 | keccak256(_resultMatch) == keccak256(_response), 57 | "response bytes mismatch" 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /docs/pages/archer-core/interfaces/IERC20.md: -------------------------------------------------------------------------------- 1 | ## `IERC20` 2 | 3 | # Functions: 4 | 5 | - [`name()`](#IERC20-name--) 6 | 7 | - [`symbol()`](#IERC20-symbol--) 8 | 9 | - [`decimals()`](#IERC20-decimals--) 10 | 11 | - [`totalSupply()`](#IERC20-totalSupply--) 12 | 13 | - [`balanceOf(address account)`](#IERC20-balanceOf-address-) 14 | 15 | - [`transfer(address recipient, uint256 amount)`](#IERC20-transfer-address-uint256-) 16 | 17 | - [`allowance(address owner, address spender)`](#IERC20-allowance-address-address-) 18 | 19 | - [`approve(address spender, uint256 amount)`](#IERC20-approve-address-uint256-) 20 | 21 | - [`transferFrom(address sender, address recipient, uint256 amount)`](#IERC20-transferFrom-address-address-uint256-) 22 | 23 | # Events: 24 | 25 | - [`Transfer(address from, address to, uint256 value)`](#IERC20-Transfer-address-address-uint256-) 26 | 27 | - [`Approval(address owner, address spender, uint256 value)`](#IERC20-Approval-address-address-uint256-) 28 | 29 | # Function `name() → string` {#IERC20-name--} 30 | 31 | No description 32 | 33 | # Function `symbol() → string` {#IERC20-symbol--} 34 | 35 | No description 36 | 37 | # Function `decimals() → uint8` {#IERC20-decimals--} 38 | 39 | No description 40 | 41 | # Function `totalSupply() → uint256` {#IERC20-totalSupply--} 42 | 43 | No description 44 | 45 | # Function `balanceOf(address account) → uint256` {#IERC20-balanceOf-address-} 46 | 47 | No description 48 | 49 | # Function `transfer(address recipient, uint256 amount) → bool` {#IERC20-transfer-address-uint256-} 50 | 51 | No description 52 | 53 | # Function `allowance(address owner, address spender) → uint256` {#IERC20-allowance-address-address-} 54 | 55 | No description 56 | 57 | # Function `approve(address spender, uint256 amount) → bool` {#IERC20-approve-address-uint256-} 58 | 59 | No description 60 | 61 | # Function `transferFrom(address sender, address recipient, uint256 amount) → bool` {#IERC20-transferFrom-address-address-uint256-} 62 | 63 | No description 64 | 65 | # Event `Transfer(address from, address to, uint256 value)` {#IERC20-Transfer-address-address-uint256-} 66 | 67 | No description 68 | 69 | # Event `Approval(address owner, address spender, uint256 value)` {#IERC20-Approval-address-address-uint256-} 70 | 71 | No description 72 | -------------------------------------------------------------------------------- /abis/IAccessControl.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes32", 6 | "name": "role", 7 | "type": "bytes32" 8 | } 9 | ], 10 | "name": "getRoleAdmin", 11 | "outputs": [ 12 | { 13 | "internalType": "bytes32", 14 | "name": "", 15 | "type": "bytes32" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | }, 21 | { 22 | "inputs": [ 23 | { 24 | "internalType": "bytes32", 25 | "name": "role", 26 | "type": "bytes32" 27 | }, 28 | { 29 | "internalType": "address", 30 | "name": "account", 31 | "type": "address" 32 | } 33 | ], 34 | "name": "grantRole", 35 | "outputs": [], 36 | "stateMutability": "nonpayable", 37 | "type": "function" 38 | }, 39 | { 40 | "inputs": [ 41 | { 42 | "internalType": "bytes32", 43 | "name": "role", 44 | "type": "bytes32" 45 | }, 46 | { 47 | "internalType": "address", 48 | "name": "account", 49 | "type": "address" 50 | } 51 | ], 52 | "name": "hasRole", 53 | "outputs": [ 54 | { 55 | "internalType": "bool", 56 | "name": "", 57 | "type": "bool" 58 | } 59 | ], 60 | "stateMutability": "view", 61 | "type": "function" 62 | }, 63 | { 64 | "inputs": [ 65 | { 66 | "internalType": "bytes32", 67 | "name": "role", 68 | "type": "bytes32" 69 | }, 70 | { 71 | "internalType": "address", 72 | "name": "account", 73 | "type": "address" 74 | } 75 | ], 76 | "name": "renounceRole", 77 | "outputs": [], 78 | "stateMutability": "nonpayable", 79 | "type": "function" 80 | }, 81 | { 82 | "inputs": [ 83 | { 84 | "internalType": "bytes32", 85 | "name": "role", 86 | "type": "bytes32" 87 | }, 88 | { 89 | "internalType": "address", 90 | "name": "account", 91 | "type": "address" 92 | } 93 | ], 94 | "name": "revokeRole", 95 | "outputs": [], 96 | "stateMutability": "nonpayable", 97 | "type": "function" 98 | } 99 | ] 100 | -------------------------------------------------------------------------------- /abis/IAccessControlUpgradeable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes32", 6 | "name": "role", 7 | "type": "bytes32" 8 | } 9 | ], 10 | "name": "getRoleAdmin", 11 | "outputs": [ 12 | { 13 | "internalType": "bytes32", 14 | "name": "", 15 | "type": "bytes32" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | }, 21 | { 22 | "inputs": [ 23 | { 24 | "internalType": "bytes32", 25 | "name": "role", 26 | "type": "bytes32" 27 | }, 28 | { 29 | "internalType": "address", 30 | "name": "account", 31 | "type": "address" 32 | } 33 | ], 34 | "name": "grantRole", 35 | "outputs": [], 36 | "stateMutability": "nonpayable", 37 | "type": "function" 38 | }, 39 | { 40 | "inputs": [ 41 | { 42 | "internalType": "bytes32", 43 | "name": "role", 44 | "type": "bytes32" 45 | }, 46 | { 47 | "internalType": "address", 48 | "name": "account", 49 | "type": "address" 50 | } 51 | ], 52 | "name": "hasRole", 53 | "outputs": [ 54 | { 55 | "internalType": "bool", 56 | "name": "", 57 | "type": "bool" 58 | } 59 | ], 60 | "stateMutability": "view", 61 | "type": "function" 62 | }, 63 | { 64 | "inputs": [ 65 | { 66 | "internalType": "bytes32", 67 | "name": "role", 68 | "type": "bytes32" 69 | }, 70 | { 71 | "internalType": "address", 72 | "name": "account", 73 | "type": "address" 74 | } 75 | ], 76 | "name": "renounceRole", 77 | "outputs": [], 78 | "stateMutability": "nonpayable", 79 | "type": "function" 80 | }, 81 | { 82 | "inputs": [ 83 | { 84 | "internalType": "bytes32", 85 | "name": "role", 86 | "type": "bytes32" 87 | }, 88 | { 89 | "internalType": "address", 90 | "name": "account", 91 | "type": "address" 92 | } 93 | ], 94 | "name": "revokeRole", 95 | "outputs": [], 96 | "stateMutability": "nonpayable", 97 | "type": "function" 98 | } 99 | ] 100 | -------------------------------------------------------------------------------- /abis/ITipJar.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "newAdmin", 7 | "type": "address" 8 | } 9 | ], 10 | "name": "changeAdmin", 11 | "outputs": [], 12 | "stateMutability": "nonpayable", 13 | "type": "function" 14 | }, 15 | { 16 | "inputs": [ 17 | { 18 | "internalType": "uint32", 19 | "name": "newFee", 20 | "type": "uint32" 21 | } 22 | ], 23 | "name": "setFee", 24 | "outputs": [], 25 | "stateMutability": "nonpayable", 26 | "type": "function" 27 | }, 28 | { 29 | "inputs": [ 30 | { 31 | "internalType": "address", 32 | "name": "newCollector", 33 | "type": "address" 34 | } 35 | ], 36 | "name": "setFeeCollector", 37 | "outputs": [], 38 | "stateMutability": "nonpayable", 39 | "type": "function" 40 | }, 41 | { 42 | "inputs": [], 43 | "name": "tip", 44 | "outputs": [], 45 | "stateMutability": "payable", 46 | "type": "function" 47 | }, 48 | { 49 | "inputs": [ 50 | { 51 | "internalType": "address", 52 | "name": "minerAddress", 53 | "type": "address" 54 | }, 55 | { 56 | "internalType": "address", 57 | "name": "splitTo", 58 | "type": "address" 59 | }, 60 | { 61 | "internalType": "uint32", 62 | "name": "splitPct", 63 | "type": "uint32" 64 | } 65 | ], 66 | "name": "updateMinerSplit", 67 | "outputs": [], 68 | "stateMutability": "nonpayable", 69 | "type": "function" 70 | }, 71 | { 72 | "inputs": [ 73 | { 74 | "internalType": "address", 75 | "name": "newImplementation", 76 | "type": "address" 77 | } 78 | ], 79 | "name": "upgradeTo", 80 | "outputs": [], 81 | "stateMutability": "nonpayable", 82 | "type": "function" 83 | }, 84 | { 85 | "inputs": [ 86 | { 87 | "internalType": "address", 88 | "name": "newImplementation", 89 | "type": "address" 90 | }, 91 | { 92 | "internalType": "bytes", 93 | "name": "data", 94 | "type": "bytes" 95 | } 96 | ], 97 | "name": "upgradeToAndCall", 98 | "outputs": [], 99 | "stateMutability": "payable", 100 | "type": "function" 101 | } 102 | ] 103 | -------------------------------------------------------------------------------- /docs/pages/archer-core/DispatcherFactory.md: -------------------------------------------------------------------------------- 1 | ## `DispatcherFactory` 2 | 3 | # Functions: 4 | 5 | - [`constructor(address _roleAdmin, address _dispatcherAdmin)`](#DispatcherFactory-constructor-address-address-) 6 | 7 | - [`createNewDispatcher(address queryEngine, address roleManager, address lpManager, address withdrawer, address trader, address approver, uint256 initialMaxLiquidity, address[] lpWhitelist)`](#DispatcherFactory-createNewDispatcher-address-address-address-address-address-address-uint256-address---) 8 | 9 | # Events: 10 | 11 | - [`DispatcherCreated(address dispatcher, uint8 version, address queryEngine, address roleManager, address lpManager, address withdrawer, address trader, address approver, uint256 initialMaxLiquidity, bool lpWhitelist)`](#DispatcherFactory-DispatcherCreated-address-uint8-address-address-address-address-address-address-uint256-bool-) 12 | 13 | # Function `constructor(address _roleAdmin, address _dispatcherAdmin)` {#DispatcherFactory-constructor-address-address-} 14 | 15 | Initializes contract, setting admin 16 | 17 | ## Parameters: 18 | 19 | - `_roleAdmin`: admin in control of roles 20 | 21 | - `_dispatcherAdmin`: admin that can create new Dispatchers 22 | 23 | # Function `createNewDispatcher(address queryEngine, address roleManager, address lpManager, address withdrawer, address trader, address approver, uint256 initialMaxLiquidity, address[] lpWhitelist) → address dispatcher` {#DispatcherFactory-createNewDispatcher-address-address-address-address-address-address-uint256-address---} 24 | 25 | Create new Dispatcher contract 26 | 27 | ## Parameters: 28 | 29 | - `queryEngine`: Address of query engine contract 30 | 31 | - `roleManager`: Address allowed to manage contract roles 32 | 33 | - `lpManager`: Address allowed to manage LP whitelist 34 | 35 | - `withdrawer`: Address allowed to withdraw profit from contract 36 | 37 | - `trader`: Address allowed to make trades via this contract 38 | 39 | - `approver`: Address allowed to make approvals on contract 40 | 41 | - `initialMaxLiquidity`: Initial max liquidity allowed in contract 42 | 43 | - `lpWhitelist`: list of addresses that are allowed to provide liquidity to this contract 44 | 45 | ## Return Values: 46 | 47 | - dispatcher Address of new Dispatcher contract 48 | 49 | # Event `DispatcherCreated(address dispatcher, uint8 version, address queryEngine, address roleManager, address lpManager, address withdrawer, address trader, address approver, uint256 initialMaxLiquidity, bool lpWhitelist)` {#DispatcherFactory-DispatcherCreated-address-uint8-address-address-address-address-address-address-uint256-bool-} 50 | 51 | Create new Dispatcher event 52 | -------------------------------------------------------------------------------- /contracts/lib/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | /** 6 | * @dev Contract module that helps prevent reentrant calls to a function. 7 | * 8 | * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier 9 | * available, which can be applied to functions to make sure there are no nested 10 | * (reentrant) calls to them. 11 | * 12 | * Note that because there is a single `nonReentrant` guard, functions marked as 13 | * `nonReentrant` may not call one another. This can be worked around by making 14 | * those functions `private`, and then adding `external` `nonReentrant` entry 15 | * points to them. 16 | * 17 | * TIP: If you would like to learn more about reentrancy and alternative ways 18 | * to protect against it, check out our blog post 19 | * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. 20 | */ 21 | abstract contract ReentrancyGuard { 22 | // Booleans are more expensive than uint256 or any type that takes up a full 23 | // word because each write operation emits an extra SLOAD to first read the 24 | // slot's contents, replace the bits taken up by the boolean, and then write 25 | // back. This is the compiler's defense against contract upgrades and 26 | // pointer aliasing, and it cannot be disabled. 27 | 28 | // The values being non-zero value makes deployment a bit more expensive, 29 | // but in exchange the refund on every call to nonReentrant will be lower in 30 | // amount. Since refunds are capped to a percentage of the total 31 | // transaction's gas, it is best to keep them low in cases like this one, to 32 | // increase the likelihood of the full refund coming into effect. 33 | uint256 private constant _NOT_ENTERED = 1; 34 | uint256 private constant _ENTERED = 2; 35 | 36 | uint256 private _status; 37 | 38 | constructor () { 39 | _status = _NOT_ENTERED; 40 | } 41 | 42 | /** 43 | * @dev Prevents a contract from calling itself, directly or indirectly. 44 | * Calling a `nonReentrant` function from another `nonReentrant` 45 | * function is not supported. It is possible to prevent this from happening 46 | * by making the `nonReentrant` function external, and make it call a 47 | * `private` function that does the actual work. 48 | */ 49 | modifier nonReentrant() { 50 | // On the first call to nonReentrant, _notEntered will be true 51 | require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); 52 | 53 | // Any calls to nonReentrant after this point will fail 54 | _status = _ENTERED; 55 | 56 | _; 57 | 58 | // By storing the original value once again, a refund is triggered (see 59 | // https://eips.ethereum.org/EIPS/eip-2200) 60 | _status = _NOT_ENTERED; 61 | } 62 | } -------------------------------------------------------------------------------- /contracts/lib/CalldataEditor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | import "./BytesLib.sol"; 5 | 6 | abstract contract CalldataEditor { 7 | using BytesLib for bytes; 8 | 9 | /// @notice Returns uint from chunk of the bytecode 10 | /// @param data the compiled bytecode for the series of function calls 11 | /// @param location the current 'cursor' location within the bytecode 12 | /// @return result uint 13 | function uint256At(bytes memory data, uint256 location) pure internal returns (uint256 result) { 14 | assembly { 15 | result := mload(add(data, add(0x20, location))) 16 | } 17 | } 18 | 19 | /// @notice Returns address from chunk of the bytecode 20 | /// @param data the compiled bytecode for the series of function calls 21 | /// @param location the current 'cursor' location within the bytecode 22 | /// @return result address 23 | function addressAt(bytes memory data, uint256 location) pure internal returns (address result) { 24 | uint256 word = uint256At(data, location); 25 | assembly { 26 | result := div(and(word, 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000), 27 | 0x1000000000000000000000000) 28 | } 29 | } 30 | 31 | /// @notice Returns the start of the calldata within a chunk of the bytecode 32 | /// @param data the compiled bytecode for the series of function calls 33 | /// @param location the current 'cursor' location within the bytecode 34 | /// @return result pointer to start of calldata 35 | function locationOf(bytes memory data, uint256 location) pure internal returns (uint256 result) { 36 | assembly { 37 | result := add(data, add(0x20, location)) 38 | } 39 | } 40 | 41 | /// @notice Replace the bytes at the index location in original with new bytes 42 | /// @param original original bytes 43 | /// @param newBytes new bytes to replace in original 44 | /// @param location the index within the original bytes where to make the replacement 45 | function replaceDataAt(bytes memory original, bytes memory newBytes, uint256 location) pure internal { 46 | assembly { 47 | mstore(add(add(original, location), 0x20), mload(add(newBytes, 0x20))) 48 | } 49 | } 50 | 51 | /// @dev Get the revert message from a call 52 | /// @notice This is needed in order to get the human-readable revert message from a call 53 | /// @param res Response of the call 54 | /// @return Revert message string 55 | function getRevertMsg(bytes memory res) internal pure returns (string memory) { 56 | // If the res length is less than 68, then the transaction failed silently (without a revert message) 57 | if (res.length < 68) return 'Call failed for unknown reason'; 58 | bytes memory revertData = res.slice(4, res.length - 4); // Remove the selector which is the first 4 bytes 59 | return abi.decode(revertData, (string)); // All that remains is the revert string 60 | } 61 | } -------------------------------------------------------------------------------- /contracts/lib/0.8/ERC1967Proxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./Proxy.sol"; 6 | import "./Address.sol"; 7 | 8 | /** 9 | * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an 10 | * implementation address that can be changed. This address is stored in storage in the location specified by 11 | * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the 12 | * implementation behind the proxy. 13 | * 14 | * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see 15 | * {TransparentUpgradeableProxy}. 16 | */ 17 | contract ERC1967Proxy is Proxy { 18 | /** 19 | * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. 20 | * 21 | * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded 22 | * function call, and allows initializating the storage of the proxy like a Solidity constructor. 23 | */ 24 | constructor(address _logic, bytes memory _data) payable { 25 | assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); 26 | _setImplementation(_logic); 27 | if(_data.length > 0) { 28 | Address.functionDelegateCall(_logic, _data); 29 | } 30 | } 31 | 32 | /** 33 | * @dev Emitted when the implementation is upgraded. 34 | */ 35 | event Upgraded(address indexed implementation); 36 | 37 | /** 38 | * @dev Storage slot with the address of the current implementation. 39 | * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is 40 | * validated in the constructor. 41 | */ 42 | bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 43 | 44 | /** 45 | * @dev Returns the current implementation address. 46 | */ 47 | function _implementation() internal view virtual override returns (address impl) { 48 | bytes32 slot = _IMPLEMENTATION_SLOT; 49 | // solhint-disable-next-line no-inline-assembly 50 | assembly { 51 | impl := sload(slot) 52 | } 53 | } 54 | 55 | /** 56 | * @dev Upgrades the proxy to a new implementation. 57 | * 58 | * Emits an {Upgraded} event. 59 | */ 60 | function _upgradeTo(address newImplementation) internal virtual { 61 | _setImplementation(newImplementation); 62 | emit Upgraded(newImplementation); 63 | } 64 | 65 | /** 66 | * @dev Stores a new address in the EIP1967 implementation slot. 67 | */ 68 | function _setImplementation(address newImplementation) private { 69 | require(Address.isContract(newImplementation), "ERC1967Proxy: new implementation is not a contract"); 70 | 71 | bytes32 slot = _IMPLEMENTATION_SLOT; 72 | 73 | // solhint-disable-next-line no-inline-assembly 74 | assembly { 75 | sstore(slot, newImplementation) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /abis/TipJarProxy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_logic", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "admin_", 12 | "type": "address" 13 | }, 14 | { 15 | "internalType": "bytes", 16 | "name": "_data", 17 | "type": "bytes" 18 | } 19 | ], 20 | "stateMutability": "payable", 21 | "type": "constructor" 22 | }, 23 | { 24 | "anonymous": false, 25 | "inputs": [ 26 | { 27 | "indexed": false, 28 | "internalType": "address", 29 | "name": "previousAdmin", 30 | "type": "address" 31 | }, 32 | { 33 | "indexed": false, 34 | "internalType": "address", 35 | "name": "newAdmin", 36 | "type": "address" 37 | } 38 | ], 39 | "name": "AdminChanged", 40 | "type": "event" 41 | }, 42 | { 43 | "anonymous": false, 44 | "inputs": [ 45 | { 46 | "indexed": true, 47 | "internalType": "address", 48 | "name": "implementation", 49 | "type": "address" 50 | } 51 | ], 52 | "name": "Upgraded", 53 | "type": "event" 54 | }, 55 | { 56 | "stateMutability": "payable", 57 | "type": "fallback" 58 | }, 59 | { 60 | "inputs": [], 61 | "name": "admin", 62 | "outputs": [ 63 | { 64 | "internalType": "address", 65 | "name": "admin_", 66 | "type": "address" 67 | } 68 | ], 69 | "stateMutability": "nonpayable", 70 | "type": "function" 71 | }, 72 | { 73 | "inputs": [ 74 | { 75 | "internalType": "address", 76 | "name": "newAdmin", 77 | "type": "address" 78 | } 79 | ], 80 | "name": "changeAdmin", 81 | "outputs": [], 82 | "stateMutability": "nonpayable", 83 | "type": "function" 84 | }, 85 | { 86 | "inputs": [], 87 | "name": "implementation", 88 | "outputs": [ 89 | { 90 | "internalType": "address", 91 | "name": "implementation_", 92 | "type": "address" 93 | } 94 | ], 95 | "stateMutability": "nonpayable", 96 | "type": "function" 97 | }, 98 | { 99 | "inputs": [ 100 | { 101 | "internalType": "address", 102 | "name": "newImplementation", 103 | "type": "address" 104 | } 105 | ], 106 | "name": "upgradeTo", 107 | "outputs": [], 108 | "stateMutability": "nonpayable", 109 | "type": "function" 110 | }, 111 | { 112 | "inputs": [ 113 | { 114 | "internalType": "address", 115 | "name": "newImplementation", 116 | "type": "address" 117 | }, 118 | { 119 | "internalType": "bytes", 120 | "name": "data", 121 | "type": "bytes" 122 | } 123 | ], 124 | "name": "upgradeToAndCall", 125 | "outputs": [], 126 | "stateMutability": "payable", 127 | "type": "function" 128 | }, 129 | { 130 | "stateMutability": "payable", 131 | "type": "receive" 132 | } 133 | ] 134 | -------------------------------------------------------------------------------- /contracts/interfaces/ITimelockController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface ITimelockController { 5 | function TIMELOCK_ADMIN_ROLE() external view returns (bytes32); 6 | function PROPOSER_ROLE() external view returns (bytes32); 7 | function EXECUTOR_ROLE() external view returns (bytes32); 8 | function _DONE_TIMESTAMP() external view returns (uint256); 9 | receive() external payable; 10 | function isOperation(bytes32 id) external view returns (bool pending); 11 | function isOperationPending(bytes32 id) external view returns (bool pending); 12 | function isOperationReady(bytes32 id) external view returns (bool ready); 13 | function isOperationDone(bytes32 id) external view returns (bool done); 14 | function getTimestamp(bytes32 id) external view returns (uint256 timestamp); 15 | function getMinDelay() external view returns (uint256 duration); 16 | function hashOperation(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) external pure returns (bytes32 hash); 17 | function hashOperationBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt) external pure returns (bytes32 hash); 18 | function schedule(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt, uint256 delay) external returns (bytes32 id); 19 | function scheduleBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt, uint256 delay) external returns (bytes32 id); 20 | function cancel(bytes32 id) external; 21 | function execute(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) external payable; 22 | function executeBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt) external payable; 23 | function updateDelay(uint256 newDelay) external; 24 | function hasRole(bytes32 role, address account) external view returns (bool); 25 | function getRoleMemberCount(bytes32 role) external view returns (uint256); 26 | function getRoleMember(bytes32 role, uint256 index) external view returns (address); 27 | function getRoleAdmin(bytes32 role) external view returns (bytes32); 28 | function grantRole(bytes32 role, address account) external; 29 | function revokeRole(bytes32 role, address account) external; 30 | function renounceRole(bytes32 role, address account) external; 31 | event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, uint256 delay); 32 | event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); 33 | event Cancelled(bytes32 indexed id); 34 | event MinDelayChange(uint256 oldDuration, uint256 newDuration); 35 | event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); 36 | event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 37 | event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 38 | } -------------------------------------------------------------------------------- /contracts/lib/0.8/Proxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM 7 | * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to 8 | * be specified by overriding the virtual {_implementation} function. 9 | * 10 | * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a 11 | * different contract through the {_delegate} function. 12 | * 13 | * The success and return data of the delegated call will be returned back to the caller of the proxy. 14 | */ 15 | abstract contract Proxy { 16 | /** 17 | * @dev Delegates the current call to `implementation`. 18 | * 19 | * This function does not return to its internall call site, it will return directly to the external caller. 20 | */ 21 | function _delegate(address implementation) internal virtual { 22 | // solhint-disable-next-line no-inline-assembly 23 | assembly { 24 | // Copy msg.data. We take full control of memory in this inline assembly 25 | // block because it will not return to Solidity code. We overwrite the 26 | // Solidity scratch pad at memory position 0. 27 | calldatacopy(0, 0, calldatasize()) 28 | 29 | // Call the implementation. 30 | // out and outsize are 0 because we don't know the size yet. 31 | let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) 32 | 33 | // Copy the returned data. 34 | returndatacopy(0, 0, returndatasize()) 35 | 36 | switch result 37 | // delegatecall returns 0 on error. 38 | case 0 { revert(0, returndatasize()) } 39 | default { return(0, returndatasize()) } 40 | } 41 | } 42 | 43 | /** 44 | * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function 45 | * and {_fallback} should delegate. 46 | */ 47 | function _implementation() internal view virtual returns (address); 48 | 49 | /** 50 | * @dev Delegates the current call to the address returned by `_implementation()`. 51 | * 52 | * This function does not return to its internall call site, it will return directly to the external caller. 53 | */ 54 | function _fallback() internal virtual { 55 | _beforeFallback(); 56 | _delegate(_implementation()); 57 | } 58 | 59 | /** 60 | * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other 61 | * function in the contract matches the call data. 62 | */ 63 | fallback () external payable virtual { 64 | _fallback(); 65 | } 66 | 67 | /** 68 | * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data 69 | * is empty. 70 | */ 71 | receive () external payable virtual { 72 | _fallback(); 73 | } 74 | 75 | /** 76 | * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` 77 | * call, or as part of the Solidity `fallback` or `receive` functions. 78 | * 79 | * If overriden should call `super._beforeFallback()`. 80 | */ 81 | function _beforeFallback() internal virtual { 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archer-core", 3 | "version": "0.1.0", 4 | "description": "Core smart contracts for the Archer network", 5 | "repository": "https://github.com/archerdao/archer-core", 6 | "author": "Chris Piatt (@chpiatt)", 7 | "license": "Unlicense", 8 | "scripts": { 9 | "hardhat": "npx hardhat node --no-deploy --show-accounts", 10 | "accounts": "npx hardhat accounts", 11 | "compile": "npx hardhat compile", 12 | "script": "sh -c 'npx ./scripts/${0}.js'", 13 | "script:latest": "sh -c 'npx hardhat run ./scripts/${0}.js'", 14 | "docs": "npx ./docify.js", 15 | "test": "export REPORT_GAS=false && npx hardhat test", 16 | "test:gas": "export REPORT_GAS=true && npx hardhat test", 17 | "test:current": "export REPORT_GAS=false && npx hardhat test --no-compile", 18 | "test:current:gas": "export REPORT_GAS=true && npx hardhat test --no-compile", 19 | "test:spec": "sh -c 'export REPORT_GAS=false && npx hardhat test ./test/spec/${0}.spec.js'", 20 | "test:spec:gas": "sh -c 'export REPORT_GAS=true && npx hardhat test ./test/spec/${0}.spec.js'", 21 | "test:spec:current": "sh -c 'export REPORT_GAS=false && npx hardhat test ./test/spec/${0}.spec.js --no-compile'", 22 | "test:spec:current:gas": "sh -c 'export REPORT_GAS=true && npx hardhat test ./test/spec/${0}.spec.js --no-compile'", 23 | "deploy": "npx hardhat deploy", 24 | "deploy:reset": "npx hardhat deploy --reset", 25 | "deploy:tag": "sh -c 'npx hardhat deploy --tags ${0}'", 26 | "deploy:hardhat": "npx hardhat deploy --network hardhat", 27 | "deploy:hardhat:tag": "sh -c 'npx hardhat deploy --network hardhat --tags ${0}'", 28 | "deploy:local": "npx hardhat deploy --network localhost", 29 | "deploy:local:reset": "npx hardhat deploy --network localhost --reset", 30 | "deploy:local:tag": "sh -c 'npx hardhat deploy --network localhost --tags ${0}'", 31 | "deploy:local:tag:reset": "sh -c 'npx hardhat deploy --network localhost --tags ${0} --reset'", 32 | "deploy:rinkeby": "npx hardhat deploy --network rinkeby", 33 | "deploy:rinkeby:reset": "npx hardhat deploy --network rinkeby --reset", 34 | "deploy:rinkeby:tag": "sh -c 'npx hardhat deploy --network rinkeby --tags ${0}'", 35 | "deploy:rinkeby:tag:reset": "sh -c 'npx hardhat deploy --network rinkeby --tags ${0} --reset'", 36 | "deploy:prod": "npx hardhat deploy --network mainnet", 37 | "deploy:prod:reset": "npx hardhat deploy --network mainnet --reset", 38 | "deploy:prod:tag": "sh -c 'npx hardhat deploy --network mainnet --tags ${0}'", 39 | "deploy:prod:tag:reset": "sh -c 'npx hardhat deploy --network mainnet --tags ${0} --reset'", 40 | "logs": "npx hardhat remove-logs", 41 | "clean": "npx hardhat clean", 42 | "verify": "npx hardhat etherscan-verify --api-key $ETHERSCAN_API_KEY", 43 | "flatten": "mkdir -p flattened && npx hardhat flatten > ./flattened/Contract.sol", 44 | "tenderly:export": "sh -c 'tenderly export ${0}'", 45 | "tenderly:push": "sh -c 'npx hardhat tenderly:push ${0}=${1}'", 46 | "tenderly:verify": "sh -c 'npx hardhat tenderly:verify ${0}=${1}'" 47 | }, 48 | "devDependencies": { 49 | "@nomiclabs/hardhat-ethers": "^2.0.0", 50 | "@nomiclabs/hardhat-etherscan": "^2.0.0", 51 | "@nomiclabs/hardhat-waffle": "^2.0.0", 52 | "chai": "^4.2.0", 53 | "chalk": "^4.1.0", 54 | "ethereum-waffle": "^3.0.0", 55 | "ethers": "^5.0.0", 56 | "hardhat": "^2.0.4", 57 | "hardhat-abi-exporter": "^2.0.5", 58 | "hardhat-deploy": "^0.7.1", 59 | "hardhat-deploy-ethers": "^0.3.0-beta.7", 60 | "hardhat-gas-reporter": "^1.0.0", 61 | "hardhat-log-remover": "^2.0.0", 62 | "@tenderly/hardhat-tenderly": "^1.0.3", 63 | "solidity-docgen": "^0.5.7", 64 | "mocha": "^8.2.0" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /contracts/QueryEngine.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | import "./lib/BytesLib.sol"; 5 | import "./lib/CalldataEditor.sol"; 6 | 7 | contract QueryEngine is CalldataEditor { 8 | // Allows easy manipulation on bytes 9 | using BytesLib for bytes; 10 | 11 | /// @notice Calls the price function specified by data at contractAddress, returning the price as bytes 12 | /// @param contractAddress contract to query 13 | /// @param data the bytecode for the contract call 14 | /// @return price in bytes 15 | function getPrice(address contractAddress, bytes memory data) public view returns (bytes memory) { 16 | (bool success, bytes memory returnData) = contractAddress.staticcall(data); 17 | require(success, "Could not fetch price"); 18 | return returnData.slice(0, 32); 19 | } 20 | 21 | /// @notice Makes a series of queries at once, returning all the prices as bytes 22 | /// @param script the compiled bytecode for the series of function calls to get the final price 23 | /// @param inputLocations index locations within the script to insert input amounts dynamically 24 | /// @return all prices as bytes 25 | function queryAllPrices(bytes memory script, uint256[] memory inputLocations) public view returns (bytes memory) { 26 | uint256 location = 0; 27 | bytes memory prices; 28 | bytes memory lastPrice; 29 | bytes memory callData; 30 | uint256 inputsLength = inputLocations.length; 31 | uint256 inputsIndex = 0; 32 | while (location < script.length) { 33 | address contractAddress = addressAt(script, location); 34 | uint256 calldataLength = uint256At(script, location + 0x14); 35 | uint256 calldataStart = location + 0x14 + 0x20; 36 | if (location != 0 && inputsLength > inputsIndex) { 37 | uint256 insertLocation = inputLocations[inputsIndex]; 38 | replaceDataAt(script, lastPrice, insertLocation); 39 | inputsIndex++; 40 | } 41 | callData = script.slice(calldataStart, calldataLength); 42 | lastPrice = getPrice(contractAddress, callData); 43 | prices = prices.concat(lastPrice); 44 | location += (0x14 + 0x20 + calldataLength); 45 | } 46 | return prices; 47 | } 48 | 49 | /// @notice Makes a series of queries at once, returning the final price as a uint 50 | /// @param script the compiled bytecode for the series of function calls to get the final price 51 | /// @param inputLocations index locations within the script to insert input amounts dynamically 52 | /// @return last price as uint 53 | function query(bytes memory script, uint256[] memory inputLocations) public view returns (uint256) { 54 | uint256 location = 0; 55 | bytes memory lastPrice; 56 | bytes memory callData; 57 | uint256 inputsLength = inputLocations.length; 58 | uint256 inputsIndex = 0; 59 | while (location < script.length) { 60 | address contractAddress = addressAt(script, location); 61 | uint256 calldataLength = uint256At(script, location + 0x14); 62 | uint256 calldataStart = location + 0x14 + 0x20; 63 | if (location != 0 && inputsLength > inputsIndex) { 64 | uint256 insertLocation = inputLocations[inputsIndex]; 65 | replaceDataAt(script, lastPrice, insertLocation); 66 | inputsIndex++; 67 | } 68 | callData = script.slice(calldataStart, calldataLength); 69 | lastPrice = getPrice(contractAddress, callData); 70 | location += (0x14 + 0x20 + calldataLength); 71 | } 72 | return lastPrice.toUint256(0); 73 | } 74 | } -------------------------------------------------------------------------------- /contracts/interfaces/IDispatcher.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | import "./IQueryEngine.sol"; 5 | 6 | interface IDispatcher { 7 | function version() external view returns (uint8); 8 | function lpBalances(address) external view returns (uint256); 9 | function totalLiquidity() external view returns (uint256); 10 | function MAX_LIQUIDITY() external view returns (uint256); 11 | function tokenAllowAll(address[] memory tokensToApprove, address spender) external; 12 | function tokenAllow(address[] memory tokensToApprove, uint256[] memory approvalAmounts, address spender) external; 13 | function rescueTokens(address[] calldata tokens, uint256 amount) external; 14 | function setMaxETHLiquidity(uint256 newMax) external; 15 | function provideETHLiquidity() external payable; 16 | function removeETHLiquidity(uint256 amount) external; 17 | function withdrawEth(uint256 amount) external; 18 | function estimateQueryCost(bytes memory script, uint256[] memory inputLocations) external; 19 | function queryEngine() external view returns (IQueryEngine); 20 | function isTrader(address addressToCheck) external view returns (bool); 21 | function makeTrade(bytes memory executeScript, uint256 ethValue) external; 22 | function makeTrade(bytes memory executeScript, uint256 ethValue, uint256 blockDeadline) external; 23 | function makeTrade(bytes memory executeScript, uint256 ethValue, uint256 minTimestamp, uint256 maxTimestamp) external; 24 | function makeTrade(bytes memory queryScript, uint256[] memory queryInputLocations, bytes memory executeScript, uint256[] memory executeInputLocations, uint256 targetPrice, uint256 ethValue) external; 25 | function makeTrade(bytes memory queryScript, uint256[] memory queryInputLocations, bytes memory executeScript, uint256[] memory executeInputLocations, uint256 targetPrice, uint256 ethValue, uint256 blockDeadline) external; 26 | function makeTrade(bytes memory queryScript, uint256[] memory queryInputLocations, bytes memory executeScript, uint256[] memory executeInputLocations, uint256 targetPrice, uint256 ethValue, uint256 minTimestamp, uint256 maxTimestamp) external; 27 | function TRADER_ROLE() external view returns (bytes32); 28 | function MANAGE_LP_ROLE() external view returns (bytes32); 29 | function WHITELISTED_LP_ROLE() external view returns (bytes32); 30 | function APPROVER_ROLE() external view returns (bytes32); 31 | function WITHDRAW_ROLE() external view returns (bytes32); 32 | function DEFAULT_ADMIN_ROLE() external view returns (bytes32); 33 | function isApprover(address addressToCheck) external view returns(bool); 34 | function isWithdrawer(address addressToCheck) external view returns(bool); 35 | function isLPManager(address addressToCheck) external view returns(bool); 36 | function isWhitelistedLP(address addressToCheck) external view returns(bool); 37 | function hasRole(bytes32 role, address account) external view returns (bool); 38 | function getRoleMemberCount(bytes32 role) external view returns (uint256); 39 | function getRoleMember(bytes32 role, uint256 index) external view returns (address); 40 | function getRoleAdmin(bytes32 role) external view returns (bytes32); 41 | function grantRole(bytes32 role, address account) external; 42 | function revokeRole(bytes32 role, address account) external; 43 | function renounceRole(bytes32 role, address account) external; 44 | event MaxLiquidityUpdated(address indexed asset, uint256 indexed newAmount, uint256 oldAmount); 45 | event LiquidityProvided(address indexed asset, address indexed provider, uint256 amount); 46 | event LiquidityRemoved(address indexed asset, address indexed provider, uint256 amount); 47 | event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 48 | event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 49 | } -------------------------------------------------------------------------------- /contracts/lib/SafeERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | import "../interfaces/IERC20.sol"; 5 | import "./SafeMath.sol"; 6 | import "./Address.sol"; 7 | 8 | /** 9 | * @title SafeERC20 10 | * @dev Wrappers around ERC20 operations that throw on failure (when the token 11 | * contract returns false). Tokens that return no value (and instead revert or 12 | * throw on failure) are also supported, non-reverting calls are assumed to be 13 | * successful. 14 | * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, 15 | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. 16 | */ 17 | library SafeERC20 { 18 | using SafeMath for uint256; 19 | using Address for address; 20 | 21 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 22 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 23 | } 24 | 25 | function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { 26 | _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 27 | } 28 | 29 | /** 30 | * @dev Deprecated. This function has issues similar to the ones found in 31 | * {IERC20-approve}, and its usage is discouraged. 32 | * 33 | * Whenever possible, use {safeIncreaseAllowance} and 34 | * {safeDecreaseAllowance} instead. 35 | */ 36 | function safeApprove(IERC20 token, address spender, uint256 value) internal { 37 | // safeApprove should only be called when setting an initial allowance, 38 | // or when resetting it to zero. To increase and decrease it, use 39 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 40 | // solhint-disable-next-line max-line-length 41 | require((value == 0) || (token.allowance(address(this), spender) == 0), 42 | "SafeERC20: approve from non-zero to non-zero allowance" 43 | ); 44 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); 45 | } 46 | 47 | function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { 48 | uint256 newAllowance = token.allowance(address(this), spender).add(value); 49 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 50 | } 51 | 52 | function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { 53 | uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); 54 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 55 | } 56 | 57 | /** 58 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement 59 | * on the return value: the return value is optional (but if data is returned, it must not be false). 60 | * @param token The token targeted by the call. 61 | * @param data The call data (encoded using abi.encode or one of its variants). 62 | */ 63 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 64 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since 65 | // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that 66 | // the target address contains contract code and also asserts for success in the low-level call. 67 | 68 | bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); 69 | if (returndata.length > 0) { // Return data is optional 70 | // solhint-disable-next-line max-line-length 71 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | require("@nomiclabs/hardhat-etherscan"); 3 | require("hardhat-deploy"); 4 | require("hardhat-deploy-ethers"); 5 | require('hardhat-abi-exporter'); 6 | require('hardhat-log-remover'); 7 | require("hardhat-gas-reporter"); 8 | 9 | const INFURA_KEY = process.env.INFURA_KEY 10 | const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY 11 | const DEPLOYER_ADDRESS = process.env.DEPLOYER_ADDRESS 12 | const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY 13 | const DISPATCHER_FACTORY_ADMIN_ADDRESS = process.env.DISPATCHER_FACTORY_ADMIN_ADDRESS 14 | const DISPATCHER_FACTORY_ADMIN_PRIVATE_KEY = process.env.DISPATCHER_FACTORY_ADMIN_PRIVATE_KEY 15 | const REPORT_GAS = process.env.REPORT_GAS 16 | const CMC_API_KEY = process.env.CMC_API_KEY 17 | 18 | // Default Hardhat network config 19 | let hardhatConfig = { 20 | live: false, 21 | saveDeployments: true, 22 | tags: ["test"] 23 | } 24 | 25 | // Local network config 26 | let localhostConfig = { 27 | url: 'http://localhost:8545', 28 | live: false, 29 | saveDeployments: true, 30 | tags: ["local"] 31 | } 32 | 33 | // Rinkeby testnet config 34 | let rinkebyConfig = { 35 | url: "https://rinkeby.infura.io/v3/" + INFURA_KEY, 36 | chainId: 4, 37 | live: true, 38 | saveDeployments: true, 39 | tags: ["staging"], 40 | } 41 | 42 | // Mainnet config 43 | let mainnetConfig = { 44 | url: "https://mainnet.infura.io/v3/" + INFURA_KEY, 45 | chainId: 1, 46 | live: true, 47 | saveDeployments: true, 48 | tags: ["prod", "mainnet", "live"] 49 | } 50 | 51 | if (DEPLOYER_PRIVATE_KEY && DEPLOYER_PRIVATE_KEY.length > 0) { 52 | rinkebyConfig.accounts = [DEPLOYER_PRIVATE_KEY] 53 | mainnetConfig.accounts = [DEPLOYER_PRIVATE_KEY] 54 | if (DISPATCHER_FACTORY_ADMIN_PRIVATE_KEY && DISPATCHER_FACTORY_ADMIN_PRIVATE_KEY.length > 0) { 55 | rinkebyConfig.accounts.push(DISPATCHER_FACTORY_ADMIN_PRIVATE_KEY) 56 | mainnetConfig.accounts.push(DISPATCHER_FACTORY_ADMIN_PRIVATE_KEY) 57 | } 58 | } 59 | 60 | 61 | // Hardhat tasks 62 | // Documentation: https://hardhat.org/guides/create-task.html 63 | task("accounts", "Prints the list of accounts", async () => { 64 | const accounts = await ethers.getSigners(); 65 | 66 | for (const account of accounts) { 67 | console.log(account.address); 68 | } 69 | }); 70 | 71 | // Hardhat Config 72 | // Documentation: https://hardhat.org/config/ 73 | // Deploy add-ons: https://hardhat.org/plugins/hardhat-deploy.html 74 | module.exports = { 75 | solidity: { 76 | compilers: [ 77 | { 78 | version: "0.7.4", 79 | settings: { 80 | optimizer: { 81 | enabled: true, 82 | runs: 200 83 | } 84 | } 85 | }, 86 | { 87 | version: "0.8.3", 88 | settings: { 89 | optimizer: { 90 | enabled: true, 91 | runs: 99999 92 | } 93 | } 94 | } 95 | ] 96 | }, 97 | defaultNetwork: "hardhat", 98 | networks: { 99 | hardhat: hardhatConfig, 100 | localhost: localhostConfig, 101 | rinkeby: rinkebyConfig, 102 | mainnet: mainnetConfig 103 | }, 104 | etherscan: { 105 | apiKey: ETHERSCAN_API_KEY 106 | }, 107 | namedAccounts: { 108 | deployer: { 109 | default: 0, 110 | 1: DEPLOYER_ADDRESS, 111 | 4: DEPLOYER_ADDRESS 112 | }, 113 | admin: { 114 | default: 1, 115 | 1: DISPATCHER_FACTORY_ADMIN_ADDRESS, 116 | 4: DISPATCHER_FACTORY_ADMIN_ADDRESS 117 | } 118 | }, 119 | paths: { 120 | deploy: 'deploy', 121 | deployments: 'deployments', 122 | imports: `imports` 123 | }, 124 | abiExporter: { 125 | path: './abis', 126 | clear: true, 127 | flat: true 128 | }, 129 | gasReporter: { 130 | enabled: REPORT_GAS && REPORT_GAS == "true" ? true : false, 131 | coinmarketcap: CMC_API_KEY, 132 | currency: 'USD', 133 | showTimeSpent: true 134 | } 135 | }; -------------------------------------------------------------------------------- /docify.js: -------------------------------------------------------------------------------- 1 | const NODE_DIR = "./node_modules"; 2 | const README_FILE = "./docs/README.md"; 3 | const SUMMARY_FILE = "./docs/SUMMARY.md"; 4 | const EXCLUDE_FILE = "./docs/exclude.txt"; 5 | const TEMPLATE_DIR = "./docs/templates"; 6 | const PAGES_DIR = "./docs/pages"; 7 | const CONTRACT_INPUT_DIR = "./contracts"; 8 | const CONTRACT_OUTPUT_DIR = "./docs/pages/archer-core"; 9 | 10 | 11 | const fs = require("fs"); 12 | const path = require("path"); 13 | const { config } = require("hardhat"); 14 | const spawnSync = require("child_process").spawnSync; 15 | 16 | const relativePath = path.relative(path.dirname(SUMMARY_FILE), PAGES_DIR); 17 | 18 | let excludeInputList = [] 19 | let excludeOutputList = [] 20 | 21 | if(fs.existsSync(EXCLUDE_FILE)) { 22 | excludeInputList = lines(EXCLUDE_FILE).map(line => CONTRACT_INPUT_DIR + "/" + line); 23 | excludeOutputList = lines(EXCLUDE_FILE).map(line => CONTRACT_OUTPUT_DIR + "/" + line); 24 | } 25 | 26 | function lines(pathName) { 27 | return fs.readFileSync(pathName, {encoding: "utf8"}).split("\r").join("").split("\n"); 28 | } 29 | 30 | function formatTitle(title) { 31 | return title.replace(/\b\w/g, l => l.toUpperCase()).replace(/-|_/g, " ") 32 | } 33 | 34 | function scan(pathName, indentation) { 35 | if (!excludeInputList.includes(pathName)) { 36 | if (fs.lstatSync(pathName).isDirectory()) { 37 | if(fs.existsSync(pathName + "/README.md")) { 38 | const link = pathName.slice(PAGES_DIR.length) + "/README.md"; 39 | fs.appendFileSync(SUMMARY_FILE, indentation + "* [" + formatTitle(path.basename(pathName)) + "](" + relativePath + link + ")\n"); 40 | } else { 41 | fs.appendFileSync(SUMMARY_FILE, indentation + "* " + formatTitle(path.basename(pathName)) + "\n"); 42 | } 43 | for (const fileName of fs.readdirSync(pathName)) 44 | scan(pathName + "/" + fileName, indentation + " "); 45 | } 46 | else if (pathName.endsWith(".md") && !pathName.endsWith("README.md")) { 47 | const text = formatTitle(path.basename(pathName).slice(0, -3)); 48 | const link = pathName.slice(PAGES_DIR.length); 49 | fs.appendFileSync(SUMMARY_FILE, indentation + "* [" + text + "](" + relativePath + link + ")\n"); 50 | } 51 | } 52 | } 53 | 54 | function fix(pathName) { 55 | if (!excludeOutputList.includes(pathName)) { 56 | if (fs.lstatSync(pathName).isDirectory()) { 57 | for (const fileName of fs.readdirSync(pathName)) 58 | fix(pathName + "/" + fileName); 59 | } 60 | else if (pathName.endsWith(".md")) { 61 | fs.writeFileSync(pathName, lines(pathName).filter(line => line.trim().length > 0).join("\n\n") + "\n"); 62 | } 63 | } else { 64 | if (fs.lstatSync(pathName).isDirectory()) { 65 | fs.rmdirSync(pathName, { recursive: true }); 66 | } else { 67 | fs.unlinkSync(pathName); 68 | } 69 | } 70 | } 71 | 72 | console.log("Creating .gitbook.yaml file...") 73 | fs.writeFileSync (".gitbook.yaml", "root: ./\n"); 74 | fs.appendFileSync(".gitbook.yaml", "structure:\n"); 75 | fs.appendFileSync(".gitbook.yaml", " readme: " + README_FILE + "\n"); 76 | fs.appendFileSync(".gitbook.yaml", " summary: " + SUMMARY_FILE + "\n"); 77 | 78 | console.log("Generating contract documentation...") 79 | const args = [ 80 | NODE_DIR + "/solidity-docgen/dist/cli.js", 81 | "--input=" + CONTRACT_INPUT_DIR, 82 | "--output=" + CONTRACT_OUTPUT_DIR, 83 | "--templates=" + TEMPLATE_DIR, 84 | "--solc-module=" + NODE_DIR + "/hardhat/node_modules/solc", 85 | "--solc-settings=" + JSON.stringify(config.solidity.compilers[0].settings) 86 | ]; 87 | const result = spawnSync("node", args, {stdio: ["inherit", "inherit", "pipe"]}); 88 | if (result.stderr.length > 0) 89 | throw new Error(result.stderr); 90 | 91 | console.log("Cleaning up documentation...") 92 | fix(PAGES_DIR); 93 | 94 | console.log("Generating SUMMARY.md file (Table of Contents)...") 95 | fs.writeFileSync (SUMMARY_FILE, "# Summary\n"); 96 | for (const fileName of fs.readdirSync(PAGES_DIR)) 97 | scan(PAGES_DIR + "/" + fileName, ""); 98 | 99 | console.log("Documentation finalized.") -------------------------------------------------------------------------------- /abis/IRewardsManager.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "user", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "uint256", 14 | "name": "pid", 15 | "type": "uint256" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "amount", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Deposit", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "user", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "uint256", 39 | "name": "pid", 40 | "type": "uint256" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "amount", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "EmergencyWithdraw", 50 | "type": "event" 51 | }, 52 | { 53 | "anonymous": false, 54 | "inputs": [ 55 | { 56 | "indexed": true, 57 | "internalType": "address", 58 | "name": "user", 59 | "type": "address" 60 | }, 61 | { 62 | "indexed": true, 63 | "internalType": "uint256", 64 | "name": "pid", 65 | "type": "uint256" 66 | }, 67 | { 68 | "indexed": false, 69 | "internalType": "uint256", 70 | "name": "amount", 71 | "type": "uint256" 72 | } 73 | ], 74 | "name": "Withdraw", 75 | "type": "event" 76 | }, 77 | { 78 | "inputs": [ 79 | { 80 | "internalType": "uint256", 81 | "name": "pid", 82 | "type": "uint256" 83 | }, 84 | { 85 | "internalType": "uint256", 86 | "name": "amount", 87 | "type": "uint256" 88 | } 89 | ], 90 | "name": "deposit", 91 | "outputs": [], 92 | "stateMutability": "nonpayable", 93 | "type": "function" 94 | }, 95 | { 96 | "inputs": [ 97 | { 98 | "internalType": "uint256", 99 | "name": "pid", 100 | "type": "uint256" 101 | }, 102 | { 103 | "internalType": "uint256", 104 | "name": "amount", 105 | "type": "uint256" 106 | }, 107 | { 108 | "internalType": "uint256", 109 | "name": "deadline", 110 | "type": "uint256" 111 | }, 112 | { 113 | "internalType": "uint8", 114 | "name": "v", 115 | "type": "uint8" 116 | }, 117 | { 118 | "internalType": "bytes32", 119 | "name": "r", 120 | "type": "bytes32" 121 | }, 122 | { 123 | "internalType": "bytes32", 124 | "name": "s", 125 | "type": "bytes32" 126 | } 127 | ], 128 | "name": "depositWithPermit", 129 | "outputs": [], 130 | "stateMutability": "nonpayable", 131 | "type": "function" 132 | }, 133 | { 134 | "inputs": [ 135 | { 136 | "internalType": "uint256", 137 | "name": "pid", 138 | "type": "uint256" 139 | } 140 | ], 141 | "name": "emergencyWithdraw", 142 | "outputs": [], 143 | "stateMutability": "nonpayable", 144 | "type": "function" 145 | }, 146 | { 147 | "inputs": [], 148 | "name": "rewardToken", 149 | "outputs": [ 150 | { 151 | "internalType": "contract IERC20", 152 | "name": "", 153 | "type": "address" 154 | } 155 | ], 156 | "stateMutability": "view", 157 | "type": "function" 158 | }, 159 | { 160 | "inputs": [ 161 | { 162 | "internalType": "uint256", 163 | "name": "pid", 164 | "type": "uint256" 165 | }, 166 | { 167 | "internalType": "uint256", 168 | "name": "amount", 169 | "type": "uint256" 170 | } 171 | ], 172 | "name": "withdraw", 173 | "outputs": [], 174 | "stateMutability": "nonpayable", 175 | "type": "function" 176 | } 177 | ] 178 | -------------------------------------------------------------------------------- /scripts/createDispatcher.js: -------------------------------------------------------------------------------- 1 | const { ethers, deployments, getNamedAccounts } = require("hardhat"); 2 | 3 | const ROLE_MANAGER_ADDRESS = process.env.ROLE_MANAGER_ADDRESS 4 | const LP_MANAGER_ADDRESS = process.env.LP_MANAGER_ADDRESS 5 | const WITHDRAWER_ADDRESS = process.env.WITHDRAWER_ADDRESS 6 | const TRADER_ADDRESS = process.env.TRADER_ADDRESS 7 | const SUPPLIER_ADDRESS = process.env.SUPPLIER_ADDRESS 8 | const INITIAL_MAX_LIQUIDITY = process.env.INITIAL_MAX_LIQUIDITY 9 | let BOUNCER_ADDRESS = process.env.BOUNCER_ADDRESS 10 | 11 | let LP_WHITELIST = [SUPPLIER_ADDRESS] 12 | const ADD_BOUNCER_TO_WHITELIST = true 13 | 14 | async function getBouncerAddress() { 15 | const bouncer = await deployments.get("Bouncer") 16 | return bouncer.address 17 | } 18 | 19 | async function getQueryEngineAddress() { 20 | const queryEngine = await deployments.get('QueryEngine') 21 | return queryEngine.address 22 | } 23 | 24 | async function createDispatcher( 25 | queryEngine, 26 | roleManager, 27 | lpManager, 28 | withdrawer, 29 | trader, 30 | supplier, 31 | initialMaxLiquidity, 32 | lpWhitelist 33 | ) { 34 | const { admin } = await getNamedAccounts() 35 | console.log(`- Creating new Dispatcher`) 36 | const receipt = await deployments.execute( 37 | 'DispatcherFactory', 38 | { from: admin, gasLimit: 6000000 }, 39 | 'createNewDispatcher', 40 | queryEngine, 41 | roleManager, 42 | lpManager, 43 | withdrawer, 44 | trader, 45 | supplier, 46 | initialMaxLiquidity, 47 | lpWhitelist 48 | ); 49 | 50 | if(receipt.status) { 51 | for(const event of receipt.events) { 52 | if(event.event == 'DispatcherCreated') { 53 | console.log(`- New Dispatcher created at: ${event.args.dispatcher}`) 54 | return event.args.dispatcher; 55 | } 56 | } 57 | } else { 58 | console.log(`- Error creating new Dispatcher:`) 59 | console.log(receipt) 60 | } 61 | } 62 | 63 | if (require.main === module) { 64 | if(ADD_BOUNCER_TO_WHITELIST) { 65 | if(!BOUNCER_ADDRESS) { 66 | getBouncerAddress() 67 | .then((result) => { 68 | BOUNCER_ADDRESS = result 69 | LP_WHITELIST.push(BOUNCER_ADDRESS) 70 | getQueryEngineAddress() 71 | .then((result) => { 72 | const QUERY_ENGINE_ADDRESS = result 73 | createDispatcher( 74 | QUERY_ENGINE_ADDRESS, 75 | ROLE_MANAGER_ADDRESS, 76 | LP_MANAGER_ADDRESS, 77 | WITHDRAWER_ADDRESS, 78 | TRADER_ADDRESS, 79 | SUPPLIER_ADDRESS, 80 | INITIAL_MAX_LIQUIDITY, 81 | LP_WHITELIST 82 | ) 83 | }) 84 | }) 85 | } else { 86 | LP_WHITELIST.push(BOUNCER_ADDRESS) 87 | getQueryEngineAddress() 88 | .then((result) => { 89 | const QUERY_ENGINE_ADDRESS = result 90 | createDispatcher( 91 | QUERY_ENGINE_ADDRESS, 92 | ROLE_MANAGER_ADDRESS, 93 | LP_MANAGER_ADDRESS, 94 | WITHDRAWER_ADDRESS, 95 | TRADER_ADDRESS, 96 | SUPPLIER_ADDRESS, 97 | INITIAL_MAX_LIQUIDITY, 98 | LP_WHITELIST 99 | ) 100 | }) 101 | } 102 | 103 | } else { 104 | getQueryEngineAddress() 105 | .then((result) => { 106 | const QUERY_ENGINE_ADDRESS = result 107 | createDispatcher( 108 | QUERY_ENGINE_ADDRESS, 109 | ROLE_MANAGER_ADDRESS, 110 | LP_MANAGER_ADDRESS, 111 | WITHDRAWER_ADDRESS, 112 | TRADER_ADDRESS, 113 | SUPPLIER_ADDRESS, 114 | INITIAL_MAX_LIQUIDITY, 115 | LP_WHITELIST 116 | ) 117 | }) 118 | } 119 | 120 | } 121 | 122 | module.exports.createDispatcher = createDispatcher -------------------------------------------------------------------------------- /abis/AccessControlUpgradeable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "bytes32", 8 | "name": "role", 9 | "type": "bytes32" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "bytes32", 14 | "name": "previousAdminRole", 15 | "type": "bytes32" 16 | }, 17 | { 18 | "indexed": true, 19 | "internalType": "bytes32", 20 | "name": "newAdminRole", 21 | "type": "bytes32" 22 | } 23 | ], 24 | "name": "RoleAdminChanged", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "bytes32", 33 | "name": "role", 34 | "type": "bytes32" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "account", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": true, 44 | "internalType": "address", 45 | "name": "sender", 46 | "type": "address" 47 | } 48 | ], 49 | "name": "RoleGranted", 50 | "type": "event" 51 | }, 52 | { 53 | "anonymous": false, 54 | "inputs": [ 55 | { 56 | "indexed": true, 57 | "internalType": "bytes32", 58 | "name": "role", 59 | "type": "bytes32" 60 | }, 61 | { 62 | "indexed": true, 63 | "internalType": "address", 64 | "name": "account", 65 | "type": "address" 66 | }, 67 | { 68 | "indexed": true, 69 | "internalType": "address", 70 | "name": "sender", 71 | "type": "address" 72 | } 73 | ], 74 | "name": "RoleRevoked", 75 | "type": "event" 76 | }, 77 | { 78 | "inputs": [], 79 | "name": "DEFAULT_ADMIN_ROLE", 80 | "outputs": [ 81 | { 82 | "internalType": "bytes32", 83 | "name": "", 84 | "type": "bytes32" 85 | } 86 | ], 87 | "stateMutability": "view", 88 | "type": "function" 89 | }, 90 | { 91 | "inputs": [ 92 | { 93 | "internalType": "bytes32", 94 | "name": "role", 95 | "type": "bytes32" 96 | } 97 | ], 98 | "name": "getRoleAdmin", 99 | "outputs": [ 100 | { 101 | "internalType": "bytes32", 102 | "name": "", 103 | "type": "bytes32" 104 | } 105 | ], 106 | "stateMutability": "view", 107 | "type": "function" 108 | }, 109 | { 110 | "inputs": [ 111 | { 112 | "internalType": "bytes32", 113 | "name": "role", 114 | "type": "bytes32" 115 | }, 116 | { 117 | "internalType": "address", 118 | "name": "account", 119 | "type": "address" 120 | } 121 | ], 122 | "name": "grantRole", 123 | "outputs": [], 124 | "stateMutability": "nonpayable", 125 | "type": "function" 126 | }, 127 | { 128 | "inputs": [ 129 | { 130 | "internalType": "bytes32", 131 | "name": "role", 132 | "type": "bytes32" 133 | }, 134 | { 135 | "internalType": "address", 136 | "name": "account", 137 | "type": "address" 138 | } 139 | ], 140 | "name": "hasRole", 141 | "outputs": [ 142 | { 143 | "internalType": "bool", 144 | "name": "", 145 | "type": "bool" 146 | } 147 | ], 148 | "stateMutability": "view", 149 | "type": "function" 150 | }, 151 | { 152 | "inputs": [ 153 | { 154 | "internalType": "bytes32", 155 | "name": "role", 156 | "type": "bytes32" 157 | }, 158 | { 159 | "internalType": "address", 160 | "name": "account", 161 | "type": "address" 162 | } 163 | ], 164 | "name": "renounceRole", 165 | "outputs": [], 166 | "stateMutability": "nonpayable", 167 | "type": "function" 168 | }, 169 | { 170 | "inputs": [ 171 | { 172 | "internalType": "bytes32", 173 | "name": "role", 174 | "type": "bytes32" 175 | }, 176 | { 177 | "internalType": "address", 178 | "name": "account", 179 | "type": "address" 180 | } 181 | ], 182 | "name": "revokeRole", 183 | "outputs": [], 184 | "stateMutability": "nonpayable", 185 | "type": "function" 186 | }, 187 | { 188 | "inputs": [ 189 | { 190 | "internalType": "bytes4", 191 | "name": "interfaceId", 192 | "type": "bytes4" 193 | } 194 | ], 195 | "name": "supportsInterface", 196 | "outputs": [ 197 | { 198 | "internalType": "bool", 199 | "name": "", 200 | "type": "bool" 201 | } 202 | ], 203 | "stateMutability": "view", 204 | "type": "function" 205 | } 206 | ] 207 | -------------------------------------------------------------------------------- /abis/IERC20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "owner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "spender", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "value", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "from", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "to", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "value", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "Transfer", 50 | "type": "event" 51 | }, 52 | { 53 | "inputs": [ 54 | { 55 | "internalType": "address", 56 | "name": "owner", 57 | "type": "address" 58 | }, 59 | { 60 | "internalType": "address", 61 | "name": "spender", 62 | "type": "address" 63 | } 64 | ], 65 | "name": "allowance", 66 | "outputs": [ 67 | { 68 | "internalType": "uint256", 69 | "name": "", 70 | "type": "uint256" 71 | } 72 | ], 73 | "stateMutability": "view", 74 | "type": "function" 75 | }, 76 | { 77 | "inputs": [ 78 | { 79 | "internalType": "address", 80 | "name": "spender", 81 | "type": "address" 82 | }, 83 | { 84 | "internalType": "uint256", 85 | "name": "amount", 86 | "type": "uint256" 87 | } 88 | ], 89 | "name": "approve", 90 | "outputs": [ 91 | { 92 | "internalType": "bool", 93 | "name": "", 94 | "type": "bool" 95 | } 96 | ], 97 | "stateMutability": "nonpayable", 98 | "type": "function" 99 | }, 100 | { 101 | "inputs": [ 102 | { 103 | "internalType": "address", 104 | "name": "account", 105 | "type": "address" 106 | } 107 | ], 108 | "name": "balanceOf", 109 | "outputs": [ 110 | { 111 | "internalType": "uint256", 112 | "name": "", 113 | "type": "uint256" 114 | } 115 | ], 116 | "stateMutability": "view", 117 | "type": "function" 118 | }, 119 | { 120 | "inputs": [], 121 | "name": "decimals", 122 | "outputs": [ 123 | { 124 | "internalType": "uint8", 125 | "name": "", 126 | "type": "uint8" 127 | } 128 | ], 129 | "stateMutability": "view", 130 | "type": "function" 131 | }, 132 | { 133 | "inputs": [], 134 | "name": "name", 135 | "outputs": [ 136 | { 137 | "internalType": "string", 138 | "name": "", 139 | "type": "string" 140 | } 141 | ], 142 | "stateMutability": "view", 143 | "type": "function" 144 | }, 145 | { 146 | "inputs": [], 147 | "name": "symbol", 148 | "outputs": [ 149 | { 150 | "internalType": "string", 151 | "name": "", 152 | "type": "string" 153 | } 154 | ], 155 | "stateMutability": "view", 156 | "type": "function" 157 | }, 158 | { 159 | "inputs": [], 160 | "name": "totalSupply", 161 | "outputs": [ 162 | { 163 | "internalType": "uint256", 164 | "name": "", 165 | "type": "uint256" 166 | } 167 | ], 168 | "stateMutability": "view", 169 | "type": "function" 170 | }, 171 | { 172 | "inputs": [ 173 | { 174 | "internalType": "address", 175 | "name": "recipient", 176 | "type": "address" 177 | }, 178 | { 179 | "internalType": "uint256", 180 | "name": "amount", 181 | "type": "uint256" 182 | } 183 | ], 184 | "name": "transfer", 185 | "outputs": [ 186 | { 187 | "internalType": "bool", 188 | "name": "", 189 | "type": "bool" 190 | } 191 | ], 192 | "stateMutability": "nonpayable", 193 | "type": "function" 194 | }, 195 | { 196 | "inputs": [ 197 | { 198 | "internalType": "address", 199 | "name": "sender", 200 | "type": "address" 201 | }, 202 | { 203 | "internalType": "address", 204 | "name": "recipient", 205 | "type": "address" 206 | }, 207 | { 208 | "internalType": "uint256", 209 | "name": "amount", 210 | "type": "uint256" 211 | } 212 | ], 213 | "name": "transferFrom", 214 | "outputs": [ 215 | { 216 | "internalType": "bool", 217 | "name": "", 218 | "type": "bool" 219 | } 220 | ], 221 | "stateMutability": "nonpayable", 222 | "type": "function" 223 | } 224 | ] 225 | -------------------------------------------------------------------------------- /contracts/TipJarProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./lib/0.8/ERC1967Proxy.sol"; 6 | 7 | /** 8 | * @title TipJarProxy 9 | * @dev This contract implements a proxy that is upgradeable by an admin, compatible with the OpenZeppelin Upgradeable Transparent Proxy standard. 10 | */ 11 | contract TipJarProxy is ERC1967Proxy { 12 | /** 13 | * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and 14 | * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}. 15 | */ 16 | constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) { 17 | assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); 18 | _setAdmin(admin_); 19 | } 20 | 21 | /** 22 | * @dev Emitted when the admin account has changed. 23 | */ 24 | event AdminChanged(address previousAdmin, address newAdmin); 25 | 26 | /** 27 | * @dev Storage slot with the admin of the contract. 28 | * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is 29 | * validated in the constructor. 30 | */ 31 | bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; 32 | 33 | /** 34 | * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. 35 | */ 36 | modifier ifAdmin() { 37 | if (msg.sender == _admin()) { 38 | _; 39 | } else { 40 | _fallback(); 41 | } 42 | } 43 | 44 | /** 45 | * @dev Returns the current admin. 46 | * 47 | * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. 48 | * 49 | * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the 50 | * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. 51 | * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` 52 | */ 53 | function admin() external ifAdmin returns (address admin_) { 54 | admin_ = _admin(); 55 | } 56 | 57 | /** 58 | * @dev Returns the current implementation. 59 | * 60 | * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. 61 | * 62 | * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the 63 | * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. 64 | * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` 65 | */ 66 | function implementation() external ifAdmin returns (address implementation_) { 67 | implementation_ = _implementation(); 68 | } 69 | 70 | /** 71 | * @dev Changes the admin of the proxy. 72 | * 73 | * Emits an {AdminChanged} event. 74 | * 75 | * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}. 76 | */ 77 | function changeAdmin(address newAdmin) external ifAdmin { 78 | require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address"); 79 | emit AdminChanged(_admin(), newAdmin); 80 | _setAdmin(newAdmin); 81 | } 82 | 83 | /** 84 | * @dev Upgrade the implementation of the proxy. 85 | * 86 | * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. 87 | */ 88 | function upgradeTo(address newImplementation) external ifAdmin { 89 | _upgradeTo(newImplementation); 90 | } 91 | 92 | /** 93 | * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified 94 | * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the 95 | * proxied contract. 96 | * 97 | * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}. 98 | */ 99 | function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { 100 | _upgradeTo(newImplementation); 101 | Address.functionDelegateCall(newImplementation, data); 102 | } 103 | 104 | /** 105 | * @dev Returns the current admin. 106 | */ 107 | function _admin() internal view returns (address adm) { 108 | bytes32 slot = _ADMIN_SLOT; 109 | // solhint-disable-next-line no-inline-assembly 110 | assembly { 111 | adm := sload(slot) 112 | } 113 | } 114 | 115 | /** 116 | * @dev Stores a new address in the EIP1967 admin slot. 117 | */ 118 | function _setAdmin(address newAdmin) private { 119 | bytes32 slot = _ADMIN_SLOT; 120 | 121 | // solhint-disable-next-line no-inline-assembly 122 | assembly { 123 | sstore(slot, newAdmin) 124 | } 125 | } 126 | 127 | /** 128 | * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. 129 | */ 130 | function _beforeFallback() internal override { 131 | require(msg.sender != _admin(), "TipJarProxy: admin cannot fallback to proxy target"); 132 | super._beforeFallback(); 133 | } 134 | } -------------------------------------------------------------------------------- /abis/AccessControl.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "bytes32", 8 | "name": "role", 9 | "type": "bytes32" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "bytes32", 14 | "name": "previousAdminRole", 15 | "type": "bytes32" 16 | }, 17 | { 18 | "indexed": true, 19 | "internalType": "bytes32", 20 | "name": "newAdminRole", 21 | "type": "bytes32" 22 | } 23 | ], 24 | "name": "RoleAdminChanged", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "bytes32", 33 | "name": "role", 34 | "type": "bytes32" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "account", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": true, 44 | "internalType": "address", 45 | "name": "sender", 46 | "type": "address" 47 | } 48 | ], 49 | "name": "RoleGranted", 50 | "type": "event" 51 | }, 52 | { 53 | "anonymous": false, 54 | "inputs": [ 55 | { 56 | "indexed": true, 57 | "internalType": "bytes32", 58 | "name": "role", 59 | "type": "bytes32" 60 | }, 61 | { 62 | "indexed": true, 63 | "internalType": "address", 64 | "name": "account", 65 | "type": "address" 66 | }, 67 | { 68 | "indexed": true, 69 | "internalType": "address", 70 | "name": "sender", 71 | "type": "address" 72 | } 73 | ], 74 | "name": "RoleRevoked", 75 | "type": "event" 76 | }, 77 | { 78 | "inputs": [], 79 | "name": "DEFAULT_ADMIN_ROLE", 80 | "outputs": [ 81 | { 82 | "internalType": "bytes32", 83 | "name": "", 84 | "type": "bytes32" 85 | } 86 | ], 87 | "stateMutability": "view", 88 | "type": "function" 89 | }, 90 | { 91 | "inputs": [ 92 | { 93 | "internalType": "bytes32", 94 | "name": "role", 95 | "type": "bytes32" 96 | } 97 | ], 98 | "name": "getRoleAdmin", 99 | "outputs": [ 100 | { 101 | "internalType": "bytes32", 102 | "name": "", 103 | "type": "bytes32" 104 | } 105 | ], 106 | "stateMutability": "view", 107 | "type": "function" 108 | }, 109 | { 110 | "inputs": [ 111 | { 112 | "internalType": "bytes32", 113 | "name": "role", 114 | "type": "bytes32" 115 | }, 116 | { 117 | "internalType": "uint256", 118 | "name": "index", 119 | "type": "uint256" 120 | } 121 | ], 122 | "name": "getRoleMember", 123 | "outputs": [ 124 | { 125 | "internalType": "address", 126 | "name": "", 127 | "type": "address" 128 | } 129 | ], 130 | "stateMutability": "view", 131 | "type": "function" 132 | }, 133 | { 134 | "inputs": [ 135 | { 136 | "internalType": "bytes32", 137 | "name": "role", 138 | "type": "bytes32" 139 | } 140 | ], 141 | "name": "getRoleMemberCount", 142 | "outputs": [ 143 | { 144 | "internalType": "uint256", 145 | "name": "", 146 | "type": "uint256" 147 | } 148 | ], 149 | "stateMutability": "view", 150 | "type": "function" 151 | }, 152 | { 153 | "inputs": [ 154 | { 155 | "internalType": "bytes32", 156 | "name": "role", 157 | "type": "bytes32" 158 | }, 159 | { 160 | "internalType": "address", 161 | "name": "account", 162 | "type": "address" 163 | } 164 | ], 165 | "name": "grantRole", 166 | "outputs": [], 167 | "stateMutability": "nonpayable", 168 | "type": "function" 169 | }, 170 | { 171 | "inputs": [ 172 | { 173 | "internalType": "bytes32", 174 | "name": "role", 175 | "type": "bytes32" 176 | }, 177 | { 178 | "internalType": "address", 179 | "name": "account", 180 | "type": "address" 181 | } 182 | ], 183 | "name": "hasRole", 184 | "outputs": [ 185 | { 186 | "internalType": "bool", 187 | "name": "", 188 | "type": "bool" 189 | } 190 | ], 191 | "stateMutability": "view", 192 | "type": "function" 193 | }, 194 | { 195 | "inputs": [ 196 | { 197 | "internalType": "bytes32", 198 | "name": "role", 199 | "type": "bytes32" 200 | }, 201 | { 202 | "internalType": "address", 203 | "name": "account", 204 | "type": "address" 205 | } 206 | ], 207 | "name": "renounceRole", 208 | "outputs": [], 209 | "stateMutability": "nonpayable", 210 | "type": "function" 211 | }, 212 | { 213 | "inputs": [ 214 | { 215 | "internalType": "bytes32", 216 | "name": "role", 217 | "type": "bytes32" 218 | }, 219 | { 220 | "internalType": "address", 221 | "name": "account", 222 | "type": "address" 223 | } 224 | ], 225 | "name": "revokeRole", 226 | "outputs": [], 227 | "stateMutability": "nonpayable", 228 | "type": "function" 229 | } 230 | ] 231 | -------------------------------------------------------------------------------- /contracts/DispatcherFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "./lib/AccessControl.sol"; 6 | import "./Dispatcher.sol"; 7 | 8 | /** 9 | * @title DispatcherFactory 10 | * @dev Creates and keeps track of Dispatchers on the network 11 | */ 12 | contract DispatcherFactory is AccessControl { 13 | using EnumerableSet for EnumerableSet.AddressSet; 14 | 15 | /// @notice Version number of Dispatcher Factory 16 | uint8 public version = 2; 17 | 18 | /// @notice Admin role to create new Dispatchers 19 | bytes32 public constant DISPATCHER_ADMIN_ROLE = keccak256("DISPATCHER_ADMIN_ROLE"); 20 | 21 | /// @dev Record of all Dispatchers 22 | EnumerableSet.AddressSet private dispatchersSet; 23 | 24 | /// @notice Create new Dispatcher event 25 | event DispatcherCreated( 26 | address indexed dispatcher, 27 | uint8 indexed version, 28 | address queryEngine, 29 | address roleManager, 30 | address lpManager, 31 | address withdrawer, 32 | address trader, 33 | address supplier, 34 | uint256 initialMaxLiquidity, 35 | bool lpWhitelist 36 | ); 37 | 38 | /// @notice Add existing Dispatcher event 39 | event DispatcherAdded(address indexed dispatcher); 40 | 41 | /// @notice Remove existing Dispatcher event 42 | event DispatcherRemoved(address indexed dispatcher); 43 | 44 | /// @notice modifier to restrict createNewDispatcher function 45 | modifier onlyAdmin() { 46 | require(hasRole(DISPATCHER_ADMIN_ROLE, msg.sender), "Caller must have DISPATCHER_ADMIN role"); 47 | _; 48 | } 49 | 50 | /// @notice Initializes contract, setting admin 51 | /// @param _roleAdmin admin in control of roles 52 | /// @param _dispatcherAdmin admin that can create new Dispatchers 53 | constructor( 54 | address _roleAdmin, 55 | address _dispatcherAdmin 56 | ) { 57 | _setupRole(DISPATCHER_ADMIN_ROLE, _dispatcherAdmin); 58 | _setupRole(DEFAULT_ADMIN_ROLE, _roleAdmin); 59 | } 60 | 61 | /// @notice Create new Dispatcher contract 62 | /// @param queryEngine Address of query engine contract 63 | /// @param roleManager Address allowed to manage contract roles 64 | /// @param lpManager Address allowed to manage LP whitelist 65 | /// @param withdrawer Address allowed to withdraw profit from contract 66 | /// @param trader Address allowed to make trades via this contract 67 | /// @param supplier Address allowed to supply opportunities to contract 68 | /// @param initialMaxLiquidity Initial max liquidity allowed in contract 69 | /// @param lpWhitelist List of addresses that are allowed to provide liquidity to this contract 70 | /// @return dispatcher Address of new Dispatcher contract 71 | function createNewDispatcher( 72 | address queryEngine, 73 | address roleManager, 74 | address lpManager, 75 | address withdrawer, 76 | address trader, 77 | address supplier, 78 | uint256 initialMaxLiquidity, 79 | address[] memory lpWhitelist 80 | ) external onlyAdmin returns ( 81 | address dispatcher 82 | ) { 83 | Dispatcher newDispatcher = new Dispatcher( 84 | version, 85 | queryEngine, 86 | roleManager, 87 | lpManager, 88 | withdrawer, 89 | trader, 90 | supplier, 91 | initialMaxLiquidity, 92 | lpWhitelist 93 | ); 94 | 95 | dispatcher = address(newDispatcher); 96 | dispatchersSet.add(dispatcher); 97 | 98 | emit DispatcherCreated( 99 | dispatcher, 100 | version, 101 | queryEngine, 102 | roleManager, 103 | lpManager, 104 | withdrawer, 105 | trader, 106 | supplier, 107 | initialMaxLiquidity, 108 | lpWhitelist.length > 0 ? true : false 109 | ); 110 | } 111 | 112 | /** 113 | * @notice Admin function to allow addition of dispatchers created via other Dispatcher Factories 114 | * @param dispatcherContracts Array of dispatcher contract addresses 115 | */ 116 | function addDispatchers(address[] memory dispatcherContracts) external onlyAdmin { 117 | for(uint i = 0; i < dispatcherContracts.length; i++) { 118 | dispatchersSet.add(dispatcherContracts[i]); 119 | emit DispatcherAdded(dispatcherContracts[i]); 120 | } 121 | } 122 | 123 | /** 124 | * @notice Admin function to allow removal of dispatchers from Dispatcher set 125 | * @param dispatcherContracts Dispatcher contract addresses 126 | */ 127 | function removeDispatchers(address[] memory dispatcherContracts) external onlyAdmin { 128 | for(uint i = 0; i < dispatcherContracts.length; i++) { 129 | dispatchersSet.remove(dispatcherContracts[i]); 130 | emit DispatcherRemoved(dispatcherContracts[i]); 131 | } 132 | } 133 | 134 | /** 135 | * @notice Return list of Dispatcher contracts this factory indexes 136 | * @return Array of Dispatcher addresses 137 | */ 138 | function dispatchers() external view returns (address[] memory) { 139 | uint256 dispatchersLength = dispatchersSet.length(); 140 | address[] memory dispatchersArray = new address[](dispatchersLength); 141 | for(uint i = 0; i < dispatchersLength; i++) { 142 | dispatchersArray[i] = dispatchersSet.at(i); 143 | } 144 | return dispatchersArray; 145 | } 146 | 147 | /** 148 | * @notice Determine whether this factory is indexing a Dispatcher at the provided address 149 | * @param dispatcherContract Dispatcher address 150 | * @return true if Dispatcher is indexed 151 | */ 152 | function exists(address dispatcherContract) external view returns (bool) { 153 | return dispatchersSet.contains(dispatcherContract); 154 | } 155 | 156 | /** 157 | * @notice Returns the number of Dispatchers indexed by this factory 158 | * @return number of Dispatchers indexed 159 | */ 160 | function numDispatchers() external view returns (uint256) { 161 | return dispatchersSet.length(); 162 | } 163 | } -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/Trader.md: -------------------------------------------------------------------------------- 1 | ## `Trader` 2 | 3 | # Functions: 4 | 5 | - [`isTrader(address addressToCheck)`](#Trader-isTrader-address-) 6 | 7 | - [`makeTrade(bytes executeScript, uint256 ethValue)`](#Trader-makeTrade-bytes-uint256-) 8 | 9 | - [`makeTrade(bytes executeScript, uint256 ethValue, uint256 blockDeadline)`](#Trader-makeTrade-bytes-uint256-uint256-) 10 | 11 | - [`makeTrade(bytes executeScript, uint256 ethValue, uint256 minTimestamp, uint256 maxTimestamp)`](#Trader-makeTrade-bytes-uint256-uint256-uint256-) 12 | 13 | - [`makeTrade(bytes queryScript, uint256[] queryInputLocations, bytes executeScript, uint256[] executeInputLocations, uint256 targetPrice, uint256 ethValue)`](#Trader-makeTrade-bytes-uint256---bytes-uint256---uint256-uint256-) 14 | 15 | - [`makeTrade(bytes queryScript, uint256[] queryInputLocations, bytes executeScript, uint256[] executeInputLocations, uint256 targetPrice, uint256 ethValue, uint256 blockDeadline)`](#Trader-makeTrade-bytes-uint256---bytes-uint256---uint256-uint256-uint256-) 16 | 17 | - [`makeTrade(bytes queryScript, uint256[] queryInputLocations, bytes executeScript, uint256[] executeInputLocations, uint256 targetPrice, uint256 ethValue, uint256 minTimestamp, uint256 maxTimestamp)`](#Trader-makeTrade-bytes-uint256---bytes-uint256---uint256-uint256-uint256-uint256-) 18 | 19 | # Function `isTrader(address addressToCheck) → bool` {#Trader-isTrader-address-} 20 | 21 | Returns true if given address is on the list of approved traders 22 | 23 | ## Parameters: 24 | 25 | - `addressToCheck`: the address to check 26 | 27 | ## Return Values: 28 | 29 | - true if address is trader 30 | 31 | # Function `makeTrade(bytes executeScript, uint256 ethValue)` {#Trader-makeTrade-bytes-uint256-} 32 | 33 | Makes a series of trades as single transaction if profitable without query 34 | 35 | ## Parameters: 36 | 37 | - `executeScript`: the compiled bytecode for the series of function calls to execute the trade 38 | 39 | - `ethValue`: the amount of ETH to send with initial contract call 40 | 41 | # Function `makeTrade(bytes executeScript, uint256 ethValue, uint256 blockDeadline)` {#Trader-makeTrade-bytes-uint256-uint256-} 42 | 43 | Makes a series of trades as single transaction if profitable without query + block deadline 44 | 45 | ## Parameters: 46 | 47 | - `executeScript`: the compiled bytecode for the series of function calls to execute the trade 48 | 49 | - `ethValue`: the amount of ETH to send with initial contract call 50 | 51 | - `blockDeadline`: block number when trade expires 52 | 53 | # Function `makeTrade(bytes executeScript, uint256 ethValue, uint256 minTimestamp, uint256 maxTimestamp)` {#Trader-makeTrade-bytes-uint256-uint256-uint256-} 54 | 55 | Makes a series of trades as single transaction if profitable without query + within time window specified 56 | 57 | ## Parameters: 58 | 59 | - `executeScript`: the compiled bytecode for the series of function calls to execute the trade 60 | 61 | - `ethValue`: the amount of ETH to send with initial contract call 62 | 63 | - `minTimestamp`: minimum block timestamp to execute trade 64 | 65 | - `maxTimestamp`: maximum timestamp to execute trade 66 | 67 | # Function `makeTrade(bytes queryScript, uint256[] queryInputLocations, bytes executeScript, uint256[] executeInputLocations, uint256 targetPrice, uint256 ethValue)` {#Trader-makeTrade-bytes-uint256---bytes-uint256---uint256-uint256-} 68 | 69 | Makes a series of trades as single transaction if profitable 70 | 71 | ## Parameters: 72 | 73 | - `queryScript`: the compiled bytecode for the series of function calls to get the final price 74 | 75 | - `queryInputLocations`: index locations within the queryScript to insert input amounts dynamically 76 | 77 | - `executeScript`: the compiled bytecode for the series of function calls to execute the trade 78 | 79 | - `executeInputLocations`: index locations within the executeScript to insert input amounts dynamically 80 | 81 | - `targetPrice`: profit target for this trade, if ETH>ETH, this should be ethValue + gas estimate * gas price 82 | 83 | - `ethValue`: the amount of ETH to send with initial contract call 84 | 85 | # Function `makeTrade(bytes queryScript, uint256[] queryInputLocations, bytes executeScript, uint256[] executeInputLocations, uint256 targetPrice, uint256 ethValue, uint256 blockDeadline)` {#Trader-makeTrade-bytes-uint256---bytes-uint256---uint256-uint256-uint256-} 86 | 87 | Makes a series of trades as single transaction if profitable + block deadline 88 | 89 | ## Parameters: 90 | 91 | - `queryScript`: the compiled bytecode for the series of function calls to get the final price 92 | 93 | - `queryInputLocations`: index locations within the queryScript to insert input amounts dynamically 94 | 95 | - `executeScript`: the compiled bytecode for the series of function calls to execute the trade 96 | 97 | - `executeInputLocations`: index locations within the executeScript to insert input amounts dynamically 98 | 99 | - `targetPrice`: profit target for this trade, if ETH>ETH, this should be ethValue + gas estimate * gas price 100 | 101 | - `ethValue`: the amount of ETH to send with initial contract call 102 | 103 | - `blockDeadline`: block number when trade expires 104 | 105 | # Function `makeTrade(bytes queryScript, uint256[] queryInputLocations, bytes executeScript, uint256[] executeInputLocations, uint256 targetPrice, uint256 ethValue, uint256 minTimestamp, uint256 maxTimestamp)` {#Trader-makeTrade-bytes-uint256---bytes-uint256---uint256-uint256-uint256-uint256-} 106 | 107 | Makes a series of trades as single transaction if profitable + within time window specified 108 | 109 | ## Parameters: 110 | 111 | - `queryScript`: the compiled bytecode for the series of function calls to get the final price 112 | 113 | - `queryInputLocations`: index locations within the queryScript to insert input amounts dynamically 114 | 115 | - `executeScript`: the compiled bytecode for the series of function calls to execute the trade 116 | 117 | - `executeInputLocations`: index locations within the executeScript to insert input amounts dynamically 118 | 119 | - `targetPrice`: profit target for this trade, if ETH>ETH, this should be ethValue + gas estimate * gas price 120 | 121 | - `ethValue`: the amount of ETH to send with initial contract call 122 | 123 | - `minTimestamp`: minimum block timestamp to execute trade 124 | 125 | - `maxTimestamp`: maximum timestamp to execute trade 126 | -------------------------------------------------------------------------------- /docs/pages/archer-core/lib/AccessControl.md: -------------------------------------------------------------------------------- 1 | ## `AccessControl` 2 | 3 | Contract module that allows children to implement role-based access 4 | 5 | control mechanisms. 6 | 7 | Roles are referred to by their `bytes32` identifier. These should be exposed 8 | 9 | in the external API and be unique. The best way to achieve this is by 10 | 11 | using `public constant` hash digests: 12 | 13 | ``` 14 | 15 | bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); 16 | 17 | ``` 18 | 19 | Roles can be used to represent a set of permissions. To restrict access to a 20 | 21 | function call, use {hasRole}: 22 | 23 | ``` 24 | 25 | function foo() public { 26 | 27 | require(hasRole(MY_ROLE, msg.sender)); 28 | 29 | ... 30 | 31 | } 32 | 33 | ``` 34 | 35 | Roles can be granted and revoked dynamically via the {grantRole} and 36 | 37 | {revokeRole} functions. Each role has an associated admin role, and only 38 | 39 | accounts that have a role's admin role can call {grantRole} and {revokeRole}. 40 | 41 | By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means 42 | 43 | that only accounts with this role will be able to grant or revoke other 44 | 45 | roles. More complex role relationships can be created by using 46 | 47 | {_setRoleAdmin}. 48 | 49 | WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to 50 | 51 | grant and revoke this role. Extra precautions should be taken to secure 52 | 53 | accounts that have been granted it. 54 | 55 | # Functions: 56 | 57 | - [`hasRole(bytes32 role, address account)`](#AccessControl-hasRole-bytes32-address-) 58 | 59 | - [`getRoleMemberCount(bytes32 role)`](#AccessControl-getRoleMemberCount-bytes32-) 60 | 61 | - [`getRoleMember(bytes32 role, uint256 index)`](#AccessControl-getRoleMember-bytes32-uint256-) 62 | 63 | - [`getRoleAdmin(bytes32 role)`](#AccessControl-getRoleAdmin-bytes32-) 64 | 65 | - [`grantRole(bytes32 role, address account)`](#AccessControl-grantRole-bytes32-address-) 66 | 67 | - [`revokeRole(bytes32 role, address account)`](#AccessControl-revokeRole-bytes32-address-) 68 | 69 | - [`renounceRole(bytes32 role, address account)`](#AccessControl-renounceRole-bytes32-address-) 70 | 71 | # Events: 72 | 73 | - [`RoleAdminChanged(bytes32 role, bytes32 previousAdminRole, bytes32 newAdminRole)`](#AccessControl-RoleAdminChanged-bytes32-bytes32-bytes32-) 74 | 75 | - [`RoleGranted(bytes32 role, address account, address sender)`](#AccessControl-RoleGranted-bytes32-address-address-) 76 | 77 | - [`RoleRevoked(bytes32 role, address account, address sender)`](#AccessControl-RoleRevoked-bytes32-address-address-) 78 | 79 | # Function `hasRole(bytes32 role, address account) → bool` {#AccessControl-hasRole-bytes32-address-} 80 | 81 | No description 82 | 83 | Returns `true` if `account` has been granted `role`. 84 | 85 | # Function `getRoleMemberCount(bytes32 role) → uint256` {#AccessControl-getRoleMemberCount-bytes32-} 86 | 87 | No description 88 | 89 | Returns the number of accounts that have `role`. Can be used 90 | 91 | together with {getRoleMember} to enumerate all bearers of a role. 92 | 93 | # Function `getRoleMember(bytes32 role, uint256 index) → address` {#AccessControl-getRoleMember-bytes32-uint256-} 94 | 95 | No description 96 | 97 | Returns one of the accounts that have `role`. `index` must be a 98 | 99 | value between 0 and {getRoleMemberCount}, non-inclusive. 100 | 101 | Role bearers are not sorted in any particular way, and their ordering may 102 | 103 | change at any point. 104 | 105 | WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure 106 | 107 | you perform all queries on the same block. See the following 108 | 109 | https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] 110 | 111 | for more information. 112 | 113 | # Function `getRoleAdmin(bytes32 role) → bytes32` {#AccessControl-getRoleAdmin-bytes32-} 114 | 115 | No description 116 | 117 | Returns the admin role that controls `role`. See {grantRole} and 118 | 119 | {revokeRole}. 120 | 121 | To change a role's admin, use {_setRoleAdmin}. 122 | 123 | # Function `grantRole(bytes32 role, address account)` {#AccessControl-grantRole-bytes32-address-} 124 | 125 | No description 126 | 127 | Grants `role` to `account`. 128 | 129 | If `account` had not been already granted `role`, emits a {RoleGranted} 130 | 131 | event. 132 | 133 | Requirements: 134 | 135 | - the caller must have ``role``'s admin role. 136 | 137 | # Function `revokeRole(bytes32 role, address account)` {#AccessControl-revokeRole-bytes32-address-} 138 | 139 | No description 140 | 141 | Revokes `role` from `account`. 142 | 143 | If `account` had been granted `role`, emits a {RoleRevoked} event. 144 | 145 | Requirements: 146 | 147 | - the caller must have ``role``'s admin role. 148 | 149 | # Function `renounceRole(bytes32 role, address account)` {#AccessControl-renounceRole-bytes32-address-} 150 | 151 | No description 152 | 153 | Revokes `role` from the calling account. 154 | 155 | Roles are often managed via {grantRole} and {revokeRole}: this function's 156 | 157 | purpose is to provide a mechanism for accounts to lose their privileges 158 | 159 | if they are compromised (such as when a trusted device is misplaced). 160 | 161 | If the calling account had been granted `role`, emits a {RoleRevoked} 162 | 163 | event. 164 | 165 | Requirements: 166 | 167 | - the caller must be `account`. 168 | 169 | # Event `RoleAdminChanged(bytes32 role, bytes32 previousAdminRole, bytes32 newAdminRole)` {#AccessControl-RoleAdminChanged-bytes32-bytes32-bytes32-} 170 | 171 | No description 172 | 173 | Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` 174 | 175 | `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite 176 | 177 | {RoleAdminChanged} not being emitted signaling this. 178 | 179 | _Available since v3.1._ 180 | 181 | # Event `RoleGranted(bytes32 role, address account, address sender)` {#AccessControl-RoleGranted-bytes32-address-address-} 182 | 183 | No description 184 | 185 | Emitted when `account` is granted `role`. 186 | 187 | `sender` is the account that originated the contract call, an admin role 188 | 189 | bearer except when using {_setupRole}. 190 | 191 | # Event `RoleRevoked(bytes32 role, address account, address sender)` {#AccessControl-RoleRevoked-bytes32-address-address-} 192 | 193 | No description 194 | 195 | Emitted when `account` is revoked `role`. 196 | 197 | `sender` is the account that originated the contract call: 198 | 199 | - if using `revokeRole`, it is the admin role bearer 200 | 201 | - if using `renounceRole`, it is the role bearer (i.e. `account`) 202 | -------------------------------------------------------------------------------- /contracts/lib/SafeMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol 5 | // Subject to the MIT license. 6 | 7 | /** 8 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 9 | * checks. 10 | * 11 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 12 | * in bugs, because programmers usually assume that an overflow raises an 13 | * error, which is the standard behavior in high level programming languages. 14 | * `SafeMath` restores this intuition by reverting the transaction when an 15 | * operation overflows. 16 | * 17 | * Using this library instead of the unchecked operations eliminates an entire 18 | * class of bugs, so it's recommended to use it always. 19 | */ 20 | library SafeMath { 21 | /** 22 | * @dev Returns the addition of two unsigned integers, reverting on overflow. 23 | * 24 | * Counterpart to Solidity's `+` operator. 25 | * 26 | * Requirements: 27 | * - Addition cannot overflow. 28 | */ 29 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 30 | uint256 c = a + b; 31 | require(c >= a, "SafeMath: addition overflow"); 32 | 33 | return c; 34 | } 35 | 36 | /** 37 | * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow. 38 | * 39 | * Counterpart to Solidity's `+` operator. 40 | * 41 | * Requirements: 42 | * - Addition cannot overflow. 43 | */ 44 | function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 45 | uint256 c = a + b; 46 | require(c >= a, errorMessage); 47 | 48 | return c; 49 | } 50 | 51 | /** 52 | * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). 53 | * 54 | * Counterpart to Solidity's `-` operator. 55 | * 56 | * Requirements: 57 | * - Subtraction cannot underflow. 58 | */ 59 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 60 | return sub(a, b, "SafeMath: subtraction underflow"); 61 | } 62 | 63 | /** 64 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). 65 | * 66 | * Counterpart to Solidity's `-` operator. 67 | * 68 | * Requirements: 69 | * - Subtraction cannot underflow. 70 | */ 71 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 72 | require(b <= a, errorMessage); 73 | uint256 c = a - b; 74 | 75 | return c; 76 | } 77 | 78 | /** 79 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 80 | * 81 | * Counterpart to Solidity's `*` operator. 82 | * 83 | * Requirements: 84 | * - Multiplication cannot overflow. 85 | */ 86 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 87 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 88 | // benefit is lost if 'b' is also tested. 89 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 90 | if (a == 0) { 91 | return 0; 92 | } 93 | 94 | uint256 c = a * b; 95 | require(c / a == b, "SafeMath: multiplication overflow"); 96 | 97 | return c; 98 | } 99 | 100 | /** 101 | * @dev Returns the multiplication of two unsigned integers, reverting on overflow. 102 | * 103 | * Counterpart to Solidity's `*` operator. 104 | * 105 | * Requirements: 106 | * - Multiplication cannot overflow. 107 | */ 108 | function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 109 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 110 | // benefit is lost if 'b' is also tested. 111 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 112 | if (a == 0) { 113 | return 0; 114 | } 115 | 116 | uint256 c = a * b; 117 | require(c / a == b, errorMessage); 118 | 119 | return c; 120 | } 121 | 122 | /** 123 | * @dev Returns the integer division of two unsigned integers. 124 | * Reverts on division by zero. The result is rounded towards zero. 125 | * 126 | * Counterpart to Solidity's `/` operator. Note: this function uses a 127 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 128 | * uses an invalid opcode to revert (consuming all remaining gas). 129 | * 130 | * Requirements: 131 | * - The divisor cannot be zero. 132 | */ 133 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 134 | return div(a, b, "SafeMath: division by zero"); 135 | } 136 | 137 | /** 138 | * @dev Returns the integer division of two unsigned integers. 139 | * Reverts with custom message on division by zero. The result is rounded towards zero. 140 | * 141 | * Counterpart to Solidity's `/` operator. Note: this function uses a 142 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 143 | * uses an invalid opcode to revert (consuming all remaining gas). 144 | * 145 | * Requirements: 146 | * - The divisor cannot be zero. 147 | */ 148 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 149 | // Solidity only automatically asserts when dividing by 0 150 | require(b > 0, errorMessage); 151 | uint256 c = a / b; 152 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 153 | 154 | return c; 155 | } 156 | 157 | /** 158 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 159 | * Reverts when dividing by zero. 160 | * 161 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 162 | * opcode (which leaves remaining gas untouched) while Solidity uses an 163 | * invalid opcode to revert (consuming all remaining gas). 164 | * 165 | * Requirements: 166 | * - The divisor cannot be zero. 167 | */ 168 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 169 | return mod(a, b, "SafeMath: modulo by zero"); 170 | } 171 | 172 | /** 173 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 174 | * Reverts with custom message when dividing by zero. 175 | * 176 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 177 | * opcode (which leaves remaining gas untouched) while Solidity uses an 178 | * invalid opcode to revert (consuming all remaining gas). 179 | * 180 | * Requirements: 181 | * - The divisor cannot be zero. 182 | */ 183 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 184 | require(b != 0, errorMessage); 185 | return a % b; 186 | } 187 | } -------------------------------------------------------------------------------- /test/spec/Bouncer.spec.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai") 2 | const { ethers } = require("hardhat"); 3 | const { bankrollFixture } = require("../fixtures") 4 | const DISPATCHER_ABI = require("../abis/Dispatcher.json") 5 | const BANKROLL_TOKEN_ABI = require("../abis/BankrollToken.json") 6 | const VOTING_POWER_PRISM_ADDRESS = process.env.VOTING_POWER_PRISM_ADDRESS 7 | const GLOBAL_MAX_CONTRIBUTION_PCT = process.env.GLOBAL_MAX_CONTRIBUTION_PCT 8 | const DISPATCHER_MAX_CONTRIBUTION_PCT = process.env.DISPATCHER_MAX_CONTRIBUTION_PCT 9 | const BANKROLL_REQUIRED_VOTING_POWER = process.env.BANKROLL_REQUIRED_VOTING_POWER 10 | 11 | describe('Bouncer', () => { 12 | let dispatcherFactory 13 | let bouncer 14 | let queryEngine 15 | let deployer 16 | let admin 17 | let alice 18 | let bob 19 | let ZERO_ADDRESS 20 | let dispatchers = [] 21 | 22 | beforeEach(async () => { 23 | const fix = await bankrollFixture() 24 | dispatcherFactory = fix.dispatcherFactory 25 | bouncer = fix.bouncer 26 | queryEngine = fix.queryEngine 27 | deployer = fix.deployer 28 | admin = fix.admin 29 | alice = fix.alice 30 | bob = fix.bob 31 | ZERO_ADDRESS = fix.ZERO_ADDRESS 32 | 33 | let tx = await dispatcherFactory.connect(admin).createNewDispatcher(queryEngine.address, admin.address, admin.address, deployer.address, alice.address, alice.address, "100000000000000000000", [alice.address, bouncer.address]) 34 | let receipt = await tx.wait() 35 | let dispatcherAddress 36 | for(const event of receipt.events) { 37 | if(event.event == 'DispatcherCreated') { 38 | dispatcherAddress = event.args.dispatcher; 39 | } 40 | } 41 | dispatchers.push(new ethers.Contract(dispatcherAddress, DISPATCHER_ABI, deployer)) 42 | tx = await dispatcherFactory.connect(admin).createNewDispatcher(queryEngine.address, admin.address, admin.address, deployer.address, alice.address, alice.address, "200000000000000000000", [alice.address, bouncer.address]) 43 | receipt = await tx.wait() 44 | for(const event of receipt.events) { 45 | if(event.event == 'DispatcherCreated') { 46 | dispatcherAddress = event.args.dispatcher; 47 | } 48 | } 49 | dispatchers.push(new ethers.Contract(dispatcherAddress, DISPATCHER_ABI, deployer)) 50 | tx = await dispatcherFactory.connect(admin).createNewDispatcher(queryEngine.address, admin.address, admin.address, deployer.address, bob.address, bob.address, "100000000000000000000", [bob.address]) 51 | receipt = await tx.wait() 52 | for(const event of receipt.events) { 53 | if(event.event == 'DispatcherCreated') { 54 | dispatcherAddress = event.args.dispatcher; 55 | } 56 | } 57 | dispatchers.push(new ethers.Contract(dispatcherAddress, DISPATCHER_ABI, deployer)) 58 | tx = await dispatcherFactory.connect(admin).createNewDispatcher(queryEngine.address, admin.address, admin.address, deployer.address, bob.address, bob.address, "400000000000000000000", [bob.address, bouncer.address]) 59 | receipt = await tx.wait() 60 | for(const event of receipt.events) { 61 | if(event.event == 'DispatcherCreated') { 62 | dispatcherAddress = event.args.dispatcher; 63 | } 64 | } 65 | dispatchers.push(new ethers.Contract(dispatcherAddress, DISPATCHER_ABI, deployer)) 66 | tx = await dispatcherFactory.connect(admin).createNewDispatcher(queryEngine.address, admin.address, admin.address, deployer.address, alice.address, alice.address, "200000000000000000000", [alice.address, bouncer.address]) 67 | receipt = await tx.wait() 68 | for(const event of receipt.events) { 69 | if(event.event == 'DispatcherCreated') { 70 | dispatcherAddress = event.args.dispatcher; 71 | } 72 | } 73 | dispatchers.push(new ethers.Contract(dispatcherAddress, DISPATCHER_ABI, deployer)) 74 | tx = await dispatcherFactory.connect(admin).createNewDispatcher(queryEngine.address, admin.address, admin.address, deployer.address, alice.address, alice.address, "500000000000000000000", [alice.address]) 75 | receipt = await tx.wait() 76 | for(const event of receipt.events) { 77 | if(event.event == 'DispatcherCreated') { 78 | dispatcherAddress = event.args.dispatcher; 79 | } 80 | } 81 | dispatchers.push(new ethers.Contract(dispatcherAddress, DISPATCHER_ABI, deployer)) 82 | await bouncer.join(dispatchers[0].address) 83 | await bouncer.join(dispatchers[1].address) 84 | await bouncer.join(dispatchers[2].address) 85 | }) 86 | 87 | context('join', async () => { 88 | it('allows a valid join', async () => { 89 | await bouncer.join(dispatchers[3].address) 90 | expect(await bouncer.bankrollTokens(dispatchers[3].address, ZERO_ADDRESS)).to.not.eq(ZERO_ADDRESS) 91 | }) 92 | 93 | it('does not allow a dispatcher to join multiple times with same asset', async () => { 94 | const bankrollToken = await bouncer.bankrollTokens(dispatchers[0].address, ZERO_ADDRESS) 95 | expect(await bouncer.bankrollTokens(dispatchers[0].address, ZERO_ADDRESS)).to.not.eq(ZERO_ADDRESS) 96 | await bouncer.join(dispatchers[0].address) 97 | expect(await bouncer.bankrollTokens(dispatchers[0].address, ZERO_ADDRESS)).to.eq(bankrollToken) 98 | }) 99 | }) 100 | 101 | context('migrate', async () => { 102 | it('allows a valid migrate', async () => { 103 | const bankrollTokenAddress = await bouncer.bankrollTokens(dispatchers[0].address, ZERO_ADDRESS) 104 | const bankrollToken = new ethers.Contract(bankrollTokenAddress, BANKROLL_TOKEN_ABI, deployer) 105 | const BouncerFactory = await ethers.getContractFactory("Bouncer") 106 | const newBouncer = await BouncerFactory.deploy( 107 | dispatcherFactory.address, 108 | VOTING_POWER_PRISM_ADDRESS, 109 | GLOBAL_MAX_CONTRIBUTION_PCT, 110 | DISPATCHER_MAX_CONTRIBUTION_PCT, 111 | BANKROLL_REQUIRED_VOTING_POWER, 112 | admin.address, 113 | admin.address 114 | ) 115 | await bouncer.connect(admin).migrate(bankrollTokenAddress, newBouncer.address) 116 | expect(await bankrollToken.supplyManager()).to.eq(newBouncer.address) 117 | }) 118 | 119 | it('does not allow a non-admin to migrate', async () => { 120 | const bankrollTokenAddress = await bouncer.bankrollTokens(dispatchers[0].address, ZERO_ADDRESS) 121 | const BouncerFactory = await ethers.getContractFactory("Bouncer") 122 | const newBouncer = await BouncerFactory.deploy( 123 | dispatcherFactory.address, 124 | VOTING_POWER_PRISM_ADDRESS, 125 | GLOBAL_MAX_CONTRIBUTION_PCT, 126 | DISPATCHER_MAX_CONTRIBUTION_PCT, 127 | BANKROLL_REQUIRED_VOTING_POWER, 128 | admin.address, 129 | admin.address 130 | ) 131 | await expect(bouncer.migrate(bankrollTokenAddress, newBouncer.address)).to.revertedWith("Caller must have BOUNCER_ADMIN_ROLE role") 132 | }) 133 | 134 | it('cannot migrate to zero address', async () => { 135 | const bankrollTokenAddress = await bouncer.bankrollTokens(dispatchers[0].address, ZERO_ADDRESS) 136 | await expect(bouncer.connect(admin).migrate(bankrollTokenAddress, ZERO_ADDRESS)).to.revertedWith("cannot migrate to zero") 137 | }) 138 | }) 139 | }) -------------------------------------------------------------------------------- /contracts/lib/0.8/AddressUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Collection of functions related to the address type 7 | */ 8 | library AddressUpgradeable { 9 | /** 10 | * @dev Returns true if `account` is a contract. 11 | * 12 | * [IMPORTANT] 13 | * ==== 14 | * It is unsafe to assume that an address for which this function returns 15 | * false is an externally-owned account (EOA) and not a contract. 16 | * 17 | * Among others, `isContract` will return false for the following 18 | * types of addresses: 19 | * 20 | * - an externally-owned account 21 | * - a contract in construction 22 | * - an address where a contract will be created 23 | * - an address where a contract lived, but was destroyed 24 | * ==== 25 | */ 26 | function isContract(address account) internal view returns (bool) { 27 | // This method relies on extcodesize, which returns 0 for contracts in 28 | // construction, since the code is only stored at the end of the 29 | // constructor execution. 30 | 31 | uint256 size; 32 | // solhint-disable-next-line no-inline-assembly 33 | assembly { size := extcodesize(account) } 34 | return size > 0; 35 | } 36 | 37 | /** 38 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 39 | * `recipient`, forwarding all available gas and reverting on errors. 40 | * 41 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 42 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 43 | * imposed by `transfer`, making them unable to receive funds via 44 | * `transfer`. {sendValue} removes this limitation. 45 | * 46 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 47 | * 48 | * IMPORTANT: because control is transferred to `recipient`, care must be 49 | * taken to not create reentrancy vulnerabilities. Consider using 50 | * {ReentrancyGuard} or the 51 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 52 | */ 53 | function sendValue(address payable recipient, uint256 amount) internal { 54 | require(address(this).balance >= amount, "Address: insufficient balance"); 55 | 56 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 57 | (bool success, ) = recipient.call{ value: amount }(""); 58 | require(success, "Address: unable to send value, recipient may have reverted"); 59 | } 60 | 61 | /** 62 | * @dev Performs a Solidity function call using a low level `call`. A 63 | * plain`call` is an unsafe replacement for a function call: use this 64 | * function instead. 65 | * 66 | * If `target` reverts with a revert reason, it is bubbled up by this 67 | * function (like regular Solidity function calls). 68 | * 69 | * Returns the raw returned data. To convert to the expected return value, 70 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 71 | * 72 | * Requirements: 73 | * 74 | * - `target` must be a contract. 75 | * - calling `target` with `data` must not revert. 76 | * 77 | * _Available since v3.1._ 78 | */ 79 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 80 | return functionCall(target, data, "Address: low-level call failed"); 81 | } 82 | 83 | /** 84 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 85 | * `errorMessage` as a fallback revert reason when `target` reverts. 86 | * 87 | * _Available since v3.1._ 88 | */ 89 | function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 90 | return functionCallWithValue(target, data, 0, errorMessage); 91 | } 92 | 93 | /** 94 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 95 | * but also transferring `value` wei to `target`. 96 | * 97 | * Requirements: 98 | * 99 | * - the calling contract must have an ETH balance of at least `value`. 100 | * - the called Solidity function must be `payable`. 101 | * 102 | * _Available since v3.1._ 103 | */ 104 | function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { 105 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 106 | } 107 | 108 | /** 109 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 110 | * with `errorMessage` as a fallback revert reason when `target` reverts. 111 | * 112 | * _Available since v3.1._ 113 | */ 114 | function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { 115 | require(address(this).balance >= value, "Address: insufficient balance for call"); 116 | require(isContract(target), "Address: call to non-contract"); 117 | 118 | // solhint-disable-next-line avoid-low-level-calls 119 | (bool success, bytes memory returndata) = target.call{ value: value }(data); 120 | return _verifyCallResult(success, returndata, errorMessage); 121 | } 122 | 123 | /** 124 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 125 | * but performing a static call. 126 | * 127 | * _Available since v3.3._ 128 | */ 129 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 130 | return functionStaticCall(target, data, "Address: low-level static call failed"); 131 | } 132 | 133 | /** 134 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 135 | * but performing a static call. 136 | * 137 | * _Available since v3.3._ 138 | */ 139 | function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { 140 | require(isContract(target), "Address: static call to non-contract"); 141 | 142 | // solhint-disable-next-line avoid-low-level-calls 143 | (bool success, bytes memory returndata) = target.staticcall(data); 144 | return _verifyCallResult(success, returndata, errorMessage); 145 | } 146 | 147 | function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { 148 | if (success) { 149 | return returndata; 150 | } else { 151 | // Look for revert reason and bubble it up if present 152 | if (returndata.length > 0) { 153 | // The easiest way to bubble the revert reason is using memory via assembly 154 | 155 | // solhint-disable-next-line no-inline-assembly 156 | assembly { 157 | let returndata_size := mload(returndata) 158 | revert(add(32, returndata), returndata_size) 159 | } 160 | } else { 161 | revert(errorMessage); 162 | } 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /docs/pages/archer-core/Dispatcher.md: -------------------------------------------------------------------------------- 1 | ## `Dispatcher` 2 | 3 | # Functions: 4 | 5 | - [`constructor(uint8 _version, address _queryEngine, address _roleManager, address _lpManager, address _withdrawer, address _trader, address _approver, uint256 _initialMaxLiquidity, address[] _lpWhitelist)`](#Dispatcher-constructor-uint8-address-address-address-address-address-address-uint256-address---) 6 | 7 | - [`receive()`](#Dispatcher-receive--) 8 | 9 | - [`fallback()`](#Dispatcher-fallback--) 10 | 11 | - [`isApprover(address addressToCheck)`](#Dispatcher-isApprover-address-) 12 | 13 | - [`isWithdrawer(address addressToCheck)`](#Dispatcher-isWithdrawer-address-) 14 | 15 | - [`isLPManager(address addressToCheck)`](#Dispatcher-isLPManager-address-) 16 | 17 | - [`isWhitelistedLP(address addressToCheck)`](#Dispatcher-isWhitelistedLP-address-) 18 | 19 | - [`tokenAllowAll(address[] tokensToApprove, address spender)`](#Dispatcher-tokenAllowAll-address---address-) 20 | 21 | - [`tokenAllow(address[] tokensToApprove, uint256[] approvalAmounts, address spender)`](#Dispatcher-tokenAllow-address---uint256---address-) 22 | 23 | - [`rescueTokens(address[] tokens, uint256 amount)`](#Dispatcher-rescueTokens-address---uint256-) 24 | 25 | - [`setMaxETHLiquidity(uint256 newMax)`](#Dispatcher-setMaxETHLiquidity-uint256-) 26 | 27 | - [`provideETHLiquidity()`](#Dispatcher-provideETHLiquidity--) 28 | 29 | - [`removeETHLiquidity(uint256 amount)`](#Dispatcher-removeETHLiquidity-uint256-) 30 | 31 | - [`withdrawEth(uint256 amount)`](#Dispatcher-withdrawEth-uint256-) 32 | 33 | - [`estimateQueryCost(bytes script, uint256[] inputLocations)`](#Dispatcher-estimateQueryCost-bytes-uint256---) 34 | 35 | # Events: 36 | 37 | - [`MaxLiquidityUpdated(address asset, uint256 newAmount, uint256 oldAmount)`](#Dispatcher-MaxLiquidityUpdated-address-uint256-uint256-) 38 | 39 | - [`LiquidityProvided(address asset, address provider, uint256 amount)`](#Dispatcher-LiquidityProvided-address-address-uint256-) 40 | 41 | - [`LiquidityRemoved(address asset, address provider, uint256 amount)`](#Dispatcher-LiquidityRemoved-address-address-uint256-) 42 | 43 | # Function `constructor(uint8 _version, address _queryEngine, address _roleManager, address _lpManager, address _withdrawer, address _trader, address _approver, uint256 _initialMaxLiquidity, address[] _lpWhitelist)` {#Dispatcher-constructor-uint8-address-address-address-address-address-address-uint256-address---} 44 | 45 | Initializes contract, setting up initial contract permissions 46 | 47 | ## Parameters: 48 | 49 | - `_version`: Version number of Dispatcher 50 | 51 | - `_queryEngine`: Address of query engine contract 52 | 53 | - `_roleManager`: Address allowed to manage contract roles 54 | 55 | - `_lpManager`: Address allowed to manage LP whitelist 56 | 57 | - `_withdrawer`: Address allowed to withdraw profit from contract 58 | 59 | - `_trader`: Address allowed to make trades via this contract 60 | 61 | - `_approver`: Address allowed to make approvals via this contract 62 | 63 | - `_initialMaxLiquidity`: Initial max liquidity allowed in contract 64 | 65 | - `_lpWhitelist`: list of addresses that are allowed to provide liquidity to this contract 66 | 67 | # Function `receive()` {#Dispatcher-receive--} 68 | 69 | Receive function to allow contract to accept ETH 70 | 71 | # Function `fallback()` {#Dispatcher-fallback--} 72 | 73 | Fallback function in case receive function is not matched 74 | 75 | # Function `isApprover(address addressToCheck) → bool` {#Dispatcher-isApprover-address-} 76 | 77 | Returns true if given address is on the list of approvers 78 | 79 | ## Parameters: 80 | 81 | - `addressToCheck`: the address to check 82 | 83 | ## Return Values: 84 | 85 | - true if address is approver 86 | 87 | # Function `isWithdrawer(address addressToCheck) → bool` {#Dispatcher-isWithdrawer-address-} 88 | 89 | Returns true if given address is on the list of approved withdrawers 90 | 91 | ## Parameters: 92 | 93 | - `addressToCheck`: the address to check 94 | 95 | ## Return Values: 96 | 97 | - true if address is withdrawer 98 | 99 | # Function `isLPManager(address addressToCheck) → bool` {#Dispatcher-isLPManager-address-} 100 | 101 | Returns true if given address is on the list of LP managers 102 | 103 | ## Parameters: 104 | 105 | - `addressToCheck`: the address to check 106 | 107 | ## Return Values: 108 | 109 | - true if address is LP manager 110 | 111 | # Function `isWhitelistedLP(address addressToCheck) → bool` {#Dispatcher-isWhitelistedLP-address-} 112 | 113 | Returns true if given address is on the list of whitelisted LPs 114 | 115 | ## Parameters: 116 | 117 | - `addressToCheck`: the address to check 118 | 119 | ## Return Values: 120 | 121 | - true if address is whitelisted 122 | 123 | # Function `tokenAllowAll(address[] tokensToApprove, address spender)` {#Dispatcher-tokenAllowAll-address---address-} 124 | 125 | Set approvals for external addresses to use Dispatcher contract tokens 126 | 127 | ## Parameters: 128 | 129 | - `tokensToApprove`: the tokens to approve 130 | 131 | - `spender`: the address to allow spending of token 132 | 133 | # Function `tokenAllow(address[] tokensToApprove, uint256[] approvalAmounts, address spender)` {#Dispatcher-tokenAllow-address---uint256---address-} 134 | 135 | Set approvals for external addresses to use Dispatcher contract tokens 136 | 137 | ## Parameters: 138 | 139 | - `tokensToApprove`: the tokens to approve 140 | 141 | - `approvalAmounts`: the token approval amounts 142 | 143 | - `spender`: the address to allow spending of token 144 | 145 | # Function `rescueTokens(address[] tokens, uint256 amount)` {#Dispatcher-rescueTokens-address---uint256-} 146 | 147 | Rescue (withdraw) tokens from the smart contract 148 | 149 | ## Parameters: 150 | 151 | - `tokens`: the tokens to withdraw 152 | 153 | - `amount`: the amount of each token to withdraw. If zero, withdraws the maximum allowed amount for each token 154 | 155 | # Function `setMaxETHLiquidity(uint256 newMax)` {#Dispatcher-setMaxETHLiquidity-uint256-} 156 | 157 | Set max ETH liquidity to accept for this contract 158 | 159 | ## Parameters: 160 | 161 | - `newMax`: new max ETH liquidity 162 | 163 | # Function `provideETHLiquidity()` {#Dispatcher-provideETHLiquidity--} 164 | 165 | Provide ETH liquidity to Dispatcher 166 | 167 | # Function `removeETHLiquidity(uint256 amount)` {#Dispatcher-removeETHLiquidity-uint256-} 168 | 169 | Remove ETH liquidity from Dispatcher 170 | 171 | ## Parameters: 172 | 173 | - `amount`: amount of liquidity to remove 174 | 175 | # Function `withdrawEth(uint256 amount)` {#Dispatcher-withdrawEth-uint256-} 176 | 177 | Withdraw ETH from the smart contract 178 | 179 | ## Parameters: 180 | 181 | - `amount`: the amount of ETH to withdraw. If zero, withdraws the maximum allowed amount. 182 | 183 | # Function `estimateQueryCost(bytes script, uint256[] inputLocations)` {#Dispatcher-estimateQueryCost-bytes-uint256---} 184 | 185 | A non-view function to help estimate the cost of a given query in practice 186 | 187 | ## Parameters: 188 | 189 | - `script`: the compiled bytecode for the series of function calls to get the final price 190 | 191 | - `inputLocations`: index locations within the script to insert input amounts dynamically 192 | 193 | # Event `MaxLiquidityUpdated(address asset, uint256 newAmount, uint256 oldAmount)` {#Dispatcher-MaxLiquidityUpdated-address-uint256-uint256-} 194 | 195 | Max liquidity updated event 196 | 197 | # Event `LiquidityProvided(address asset, address provider, uint256 amount)` {#Dispatcher-LiquidityProvided-address-address-uint256-} 198 | 199 | Liquidity Provided event 200 | 201 | # Event `LiquidityRemoved(address asset, address provider, uint256 amount)` {#Dispatcher-LiquidityRemoved-address-address-uint256-} 202 | 203 | Liquidity removed event 204 | -------------------------------------------------------------------------------- /contracts/lib/0.8/AccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./Context.sol"; 6 | import "./ERC165.sol"; 7 | 8 | /** 9 | * @dev External interface of AccessControl declared to support ERC165 detection. 10 | */ 11 | interface IAccessControl { 12 | function hasRole(bytes32 role, address account) external view returns (bool); 13 | function getRoleAdmin(bytes32 role) external view returns (bytes32); 14 | function grantRole(bytes32 role, address account) external; 15 | function revokeRole(bytes32 role, address account) external; 16 | function renounceRole(bytes32 role, address account) external; 17 | } 18 | 19 | /** 20 | * @dev Contract module that allows children to implement role-based access 21 | * control mechanisms. This is a lightweight version that doesn't allow enumerating role 22 | * members except through off-chain means by accessing the contract event logs. Some 23 | * applications may benefit from on-chain enumerability, for those cases see 24 | * {AccessControlEnumerable}. 25 | * 26 | * Roles are referred to by their `bytes32` identifier. These should be exposed 27 | * in the external API and be unique. The best way to achieve this is by 28 | * using `public constant` hash digests: 29 | * 30 | * ``` 31 | * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); 32 | * ``` 33 | * 34 | * Roles can be used to represent a set of permissions. To restrict access to a 35 | * function call, use {hasRole}: 36 | * 37 | * ``` 38 | * function foo() public { 39 | * require(hasRole(MY_ROLE, msg.sender)); 40 | * ... 41 | * } 42 | * ``` 43 | * 44 | * Roles can be granted and revoked dynamically via the {grantRole} and 45 | * {revokeRole} functions. Each role has an associated admin role, and only 46 | * accounts that have a role's admin role can call {grantRole} and {revokeRole}. 47 | * 48 | * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means 49 | * that only accounts with this role will be able to grant or revoke other 50 | * roles. More complex role relationships can be created by using 51 | * {_setRoleAdmin}. 52 | * 53 | * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to 54 | * grant and revoke this role. Extra precautions should be taken to secure 55 | * accounts that have been granted it. 56 | */ 57 | abstract contract AccessControl is Context, IAccessControl, ERC165 { 58 | struct RoleData { 59 | mapping (address => bool) members; 60 | bytes32 adminRole; 61 | } 62 | 63 | mapping (bytes32 => RoleData) private _roles; 64 | 65 | bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; 66 | 67 | /** 68 | * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` 69 | * 70 | * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite 71 | * {RoleAdminChanged} not being emitted signaling this. 72 | * 73 | * _Available since v3.1._ 74 | */ 75 | event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); 76 | 77 | /** 78 | * @dev Emitted when `account` is granted `role`. 79 | * 80 | * `sender` is the account that originated the contract call, an admin role 81 | * bearer except when using {_setupRole}. 82 | */ 83 | event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 84 | 85 | /** 86 | * @dev Emitted when `account` is revoked `role`. 87 | * 88 | * `sender` is the account that originated the contract call: 89 | * - if using `revokeRole`, it is the admin role bearer 90 | * - if using `renounceRole`, it is the role bearer (i.e. `account`) 91 | */ 92 | event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 93 | 94 | /** 95 | * @dev See {IERC165-supportsInterface}. 96 | */ 97 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 98 | return interfaceId == type(IAccessControl).interfaceId 99 | || super.supportsInterface(interfaceId); 100 | } 101 | 102 | /** 103 | * @dev Returns `true` if `account` has been granted `role`. 104 | */ 105 | function hasRole(bytes32 role, address account) public view override returns (bool) { 106 | return _roles[role].members[account]; 107 | } 108 | 109 | /** 110 | * @dev Returns the admin role that controls `role`. See {grantRole} and 111 | * {revokeRole}. 112 | * 113 | * To change a role's admin, use {_setRoleAdmin}. 114 | */ 115 | function getRoleAdmin(bytes32 role) public view override returns (bytes32) { 116 | return _roles[role].adminRole; 117 | } 118 | 119 | /** 120 | * @dev Grants `role` to `account`. 121 | * 122 | * If `account` had not been already granted `role`, emits a {RoleGranted} 123 | * event. 124 | * 125 | * Requirements: 126 | * 127 | * - the caller must have ``role``'s admin role. 128 | */ 129 | function grantRole(bytes32 role, address account) public virtual override { 130 | require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant"); 131 | 132 | _grantRole(role, account); 133 | } 134 | 135 | /** 136 | * @dev Revokes `role` from `account`. 137 | * 138 | * If `account` had been granted `role`, emits a {RoleRevoked} event. 139 | * 140 | * Requirements: 141 | * 142 | * - the caller must have ``role``'s admin role. 143 | */ 144 | function revokeRole(bytes32 role, address account) public virtual override { 145 | require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke"); 146 | 147 | _revokeRole(role, account); 148 | } 149 | 150 | /** 151 | * @dev Revokes `role` from the calling account. 152 | * 153 | * Roles are often managed via {grantRole} and {revokeRole}: this function's 154 | * purpose is to provide a mechanism for accounts to lose their privileges 155 | * if they are compromised (such as when a trusted device is misplaced). 156 | * 157 | * If the calling account had been granted `role`, emits a {RoleRevoked} 158 | * event. 159 | * 160 | * Requirements: 161 | * 162 | * - the caller must be `account`. 163 | */ 164 | function renounceRole(bytes32 role, address account) public virtual override { 165 | require(account == _msgSender(), "AccessControl: can only renounce roles for self"); 166 | 167 | _revokeRole(role, account); 168 | } 169 | 170 | /** 171 | * @dev Grants `role` to `account`. 172 | * 173 | * If `account` had not been already granted `role`, emits a {RoleGranted} 174 | * event. Note that unlike {grantRole}, this function doesn't perform any 175 | * checks on the calling account. 176 | * 177 | * [WARNING] 178 | * ==== 179 | * This function should only be called from the constructor when setting 180 | * up the initial roles for the system. 181 | * 182 | * Using this function in any other way is effectively circumventing the admin 183 | * system imposed by {AccessControl}. 184 | * ==== 185 | */ 186 | function _setupRole(bytes32 role, address account) internal virtual { 187 | _grantRole(role, account); 188 | } 189 | 190 | /** 191 | * @dev Sets `adminRole` as ``role``'s admin role. 192 | * 193 | * Emits a {RoleAdminChanged} event. 194 | */ 195 | function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { 196 | emit RoleAdminChanged(role, getRoleAdmin(role), adminRole); 197 | _roles[role].adminRole = adminRole; 198 | } 199 | 200 | function _grantRole(bytes32 role, address account) private { 201 | if (!hasRole(role, account)) { 202 | _roles[role].members[account] = true; 203 | emit RoleGranted(role, account, _msgSender()); 204 | } 205 | } 206 | 207 | function _revokeRole(bytes32 role, address account) private { 208 | if (hasRole(role, account)) { 209 | _roles[role].members[account] = false; 210 | emit RoleRevoked(role, account, _msgSender()); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /contracts/lib/AccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.0 <0.8.0; 4 | 5 | import "./EnumerableSet.sol"; 6 | import "./Address.sol"; 7 | import "./Context.sol"; 8 | 9 | /** 10 | * @dev Contract module that allows children to implement role-based access 11 | * control mechanisms. 12 | * 13 | * Roles are referred to by their `bytes32` identifier. These should be exposed 14 | * in the external API and be unique. The best way to achieve this is by 15 | * using `public constant` hash digests: 16 | * 17 | * ``` 18 | * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); 19 | * ``` 20 | * 21 | * Roles can be used to represent a set of permissions. To restrict access to a 22 | * function call, use {hasRole}: 23 | * 24 | * ``` 25 | * function foo() public { 26 | * require(hasRole(MY_ROLE, msg.sender)); 27 | * ... 28 | * } 29 | * ``` 30 | * 31 | * Roles can be granted and revoked dynamically via the {grantRole} and 32 | * {revokeRole} functions. Each role has an associated admin role, and only 33 | * accounts that have a role's admin role can call {grantRole} and {revokeRole}. 34 | * 35 | * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means 36 | * that only accounts with this role will be able to grant or revoke other 37 | * roles. More complex role relationships can be created by using 38 | * {_setRoleAdmin}. 39 | * 40 | * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to 41 | * grant and revoke this role. Extra precautions should be taken to secure 42 | * accounts that have been granted it. 43 | */ 44 | abstract contract AccessControl is Context { 45 | using EnumerableSet for EnumerableSet.AddressSet; 46 | using Address for address; 47 | 48 | struct RoleData { 49 | EnumerableSet.AddressSet members; 50 | bytes32 adminRole; 51 | } 52 | 53 | mapping (bytes32 => RoleData) private _roles; 54 | 55 | bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; 56 | 57 | /** 58 | * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` 59 | * 60 | * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite 61 | * {RoleAdminChanged} not being emitted signaling this. 62 | * 63 | * _Available since v3.1._ 64 | */ 65 | event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); 66 | 67 | /** 68 | * @dev Emitted when `account` is granted `role`. 69 | * 70 | * `sender` is the account that originated the contract call, an admin role 71 | * bearer except when using {_setupRole}. 72 | */ 73 | event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 74 | 75 | /** 76 | * @dev Emitted when `account` is revoked `role`. 77 | * 78 | * `sender` is the account that originated the contract call: 79 | * - if using `revokeRole`, it is the admin role bearer 80 | * - if using `renounceRole`, it is the role bearer (i.e. `account`) 81 | */ 82 | event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 83 | 84 | /** 85 | * @dev Returns `true` if `account` has been granted `role`. 86 | */ 87 | function hasRole(bytes32 role, address account) public view returns (bool) { 88 | return _roles[role].members.contains(account); 89 | } 90 | 91 | /** 92 | * @dev Returns the number of accounts that have `role`. Can be used 93 | * together with {getRoleMember} to enumerate all bearers of a role. 94 | */ 95 | function getRoleMemberCount(bytes32 role) public view returns (uint256) { 96 | return _roles[role].members.length(); 97 | } 98 | 99 | /** 100 | * @dev Returns one of the accounts that have `role`. `index` must be a 101 | * value between 0 and {getRoleMemberCount}, non-inclusive. 102 | * 103 | * Role bearers are not sorted in any particular way, and their ordering may 104 | * change at any point. 105 | * 106 | * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure 107 | * you perform all queries on the same block. See the following 108 | * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] 109 | * for more information. 110 | */ 111 | function getRoleMember(bytes32 role, uint256 index) public view returns (address) { 112 | return _roles[role].members.at(index); 113 | } 114 | 115 | /** 116 | * @dev Returns the admin role that controls `role`. See {grantRole} and 117 | * {revokeRole}. 118 | * 119 | * To change a role's admin, use {_setRoleAdmin}. 120 | */ 121 | function getRoleAdmin(bytes32 role) public view returns (bytes32) { 122 | return _roles[role].adminRole; 123 | } 124 | 125 | /** 126 | * @dev Grants `role` to `account`. 127 | * 128 | * If `account` had not been already granted `role`, emits a {RoleGranted} 129 | * event. 130 | * 131 | * Requirements: 132 | * 133 | * - the caller must have ``role``'s admin role. 134 | */ 135 | function grantRole(bytes32 role, address account) public virtual { 136 | require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant"); 137 | 138 | _grantRole(role, account); 139 | } 140 | 141 | /** 142 | * @dev Revokes `role` from `account`. 143 | * 144 | * If `account` had been granted `role`, emits a {RoleRevoked} event. 145 | * 146 | * Requirements: 147 | * 148 | * - the caller must have ``role``'s admin role. 149 | */ 150 | function revokeRole(bytes32 role, address account) public virtual { 151 | require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke"); 152 | 153 | _revokeRole(role, account); 154 | } 155 | 156 | /** 157 | * @dev Revokes `role` from the calling account. 158 | * 159 | * Roles are often managed via {grantRole} and {revokeRole}: this function's 160 | * purpose is to provide a mechanism for accounts to lose their privileges 161 | * if they are compromised (such as when a trusted device is misplaced). 162 | * 163 | * If the calling account had been granted `role`, emits a {RoleRevoked} 164 | * event. 165 | * 166 | * Requirements: 167 | * 168 | * - the caller must be `account`. 169 | */ 170 | function renounceRole(bytes32 role, address account) public virtual { 171 | require(account == _msgSender(), "AccessControl: can only renounce roles for self"); 172 | 173 | _revokeRole(role, account); 174 | } 175 | 176 | /** 177 | * @dev Grants `role` to `account`. 178 | * 179 | * If `account` had not been already granted `role`, emits a {RoleGranted} 180 | * event. Note that unlike {grantRole}, this function doesn't perform any 181 | * checks on the calling account. 182 | * 183 | * [WARNING] 184 | * ==== 185 | * This function should only be called from the constructor when setting 186 | * up the initial roles for the system. 187 | * 188 | * Using this function in any other way is effectively circumventing the admin 189 | * system imposed by {AccessControl}. 190 | * ==== 191 | */ 192 | function _setupRole(bytes32 role, address account) internal virtual { 193 | _grantRole(role, account); 194 | } 195 | 196 | /** 197 | * @dev Sets `adminRole` as ``role``'s admin role. 198 | * 199 | * Emits a {RoleAdminChanged} event. 200 | */ 201 | function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { 202 | emit RoleAdminChanged(role, _roles[role].adminRole, adminRole); 203 | _roles[role].adminRole = adminRole; 204 | } 205 | 206 | function _grantRole(bytes32 role, address account) private { 207 | if (_roles[role].members.add(account)) { 208 | emit RoleGranted(role, account, _msgSender()); 209 | } 210 | } 211 | 212 | function _revokeRole(bytes32 role, address account) private { 213 | if (_roles[role].members.remove(account)) { 214 | emit RoleRevoked(role, account, _msgSender()); 215 | } 216 | } 217 | } -------------------------------------------------------------------------------- /contracts/lib/0.8/Address.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Collection of functions related to the address type 7 | */ 8 | library Address { 9 | /** 10 | * @dev Returns true if `account` is a contract. 11 | * 12 | * [IMPORTANT] 13 | * ==== 14 | * It is unsafe to assume that an address for which this function returns 15 | * false is an externally-owned account (EOA) and not a contract. 16 | * 17 | * Among others, `isContract` will return false for the following 18 | * types of addresses: 19 | * 20 | * - an externally-owned account 21 | * - a contract in construction 22 | * - an address where a contract will be created 23 | * - an address where a contract lived, but was destroyed 24 | * ==== 25 | */ 26 | function isContract(address account) internal view returns (bool) { 27 | // This method relies on extcodesize, which returns 0 for contracts in 28 | // construction, since the code is only stored at the end of the 29 | // constructor execution. 30 | 31 | uint256 size; 32 | // solhint-disable-next-line no-inline-assembly 33 | assembly { size := extcodesize(account) } 34 | return size > 0; 35 | } 36 | 37 | /** 38 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 39 | * `recipient`, forwarding all available gas and reverting on errors. 40 | * 41 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 42 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 43 | * imposed by `transfer`, making them unable to receive funds via 44 | * `transfer`. {sendValue} removes this limitation. 45 | * 46 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 47 | * 48 | * IMPORTANT: because control is transferred to `recipient`, care must be 49 | * taken to not create reentrancy vulnerabilities. Consider using 50 | * {ReentrancyGuard} or the 51 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 52 | */ 53 | function sendValue(address payable recipient, uint256 amount) internal { 54 | require(address(this).balance >= amount, "Address: insufficient balance"); 55 | 56 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 57 | (bool success, ) = recipient.call{ value: amount }(""); 58 | require(success, "Address: unable to send value, recipient may have reverted"); 59 | } 60 | 61 | /** 62 | * @dev Performs a Solidity function call using a low level `call`. A 63 | * plain`call` is an unsafe replacement for a function call: use this 64 | * function instead. 65 | * 66 | * If `target` reverts with a revert reason, it is bubbled up by this 67 | * function (like regular Solidity function calls). 68 | * 69 | * Returns the raw returned data. To convert to the expected return value, 70 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 71 | * 72 | * Requirements: 73 | * 74 | * - `target` must be a contract. 75 | * - calling `target` with `data` must not revert. 76 | * 77 | * _Available since v3.1._ 78 | */ 79 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 80 | return functionCall(target, data, "Address: low-level call failed"); 81 | } 82 | 83 | /** 84 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 85 | * `errorMessage` as a fallback revert reason when `target` reverts. 86 | * 87 | * _Available since v3.1._ 88 | */ 89 | function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 90 | return functionCallWithValue(target, data, 0, errorMessage); 91 | } 92 | 93 | /** 94 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 95 | * but also transferring `value` wei to `target`. 96 | * 97 | * Requirements: 98 | * 99 | * - the calling contract must have an ETH balance of at least `value`. 100 | * - the called Solidity function must be `payable`. 101 | * 102 | * _Available since v3.1._ 103 | */ 104 | function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { 105 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 106 | } 107 | 108 | /** 109 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 110 | * with `errorMessage` as a fallback revert reason when `target` reverts. 111 | * 112 | * _Available since v3.1._ 113 | */ 114 | function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { 115 | require(address(this).balance >= value, "Address: insufficient balance for call"); 116 | require(isContract(target), "Address: call to non-contract"); 117 | 118 | // solhint-disable-next-line avoid-low-level-calls 119 | (bool success, bytes memory returndata) = target.call{ value: value }(data); 120 | return _verifyCallResult(success, returndata, errorMessage); 121 | } 122 | 123 | /** 124 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 125 | * but performing a static call. 126 | * 127 | * _Available since v3.3._ 128 | */ 129 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 130 | return functionStaticCall(target, data, "Address: low-level static call failed"); 131 | } 132 | 133 | /** 134 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 135 | * but performing a static call. 136 | * 137 | * _Available since v3.3._ 138 | */ 139 | function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { 140 | require(isContract(target), "Address: static call to non-contract"); 141 | 142 | // solhint-disable-next-line avoid-low-level-calls 143 | (bool success, bytes memory returndata) = target.staticcall(data); 144 | return _verifyCallResult(success, returndata, errorMessage); 145 | } 146 | 147 | /** 148 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 149 | * but performing a delegate call. 150 | * 151 | * _Available since v3.4._ 152 | */ 153 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 154 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 155 | } 156 | 157 | /** 158 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 159 | * but performing a delegate call. 160 | * 161 | * _Available since v3.4._ 162 | */ 163 | function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 164 | require(isContract(target), "Address: delegate call to non-contract"); 165 | 166 | // solhint-disable-next-line avoid-low-level-calls 167 | (bool success, bytes memory returndata) = target.delegatecall(data); 168 | return _verifyCallResult(success, returndata, errorMessage); 169 | } 170 | 171 | function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { 172 | if (success) { 173 | return returndata; 174 | } else { 175 | // Look for revert reason and bubble it up if present 176 | if (returndata.length > 0) { 177 | // The easiest way to bubble the revert reason is using memory via assembly 178 | 179 | // solhint-disable-next-line no-inline-assembly 180 | assembly { 181 | let returndata_size := mload(returndata) 182 | revert(add(32, returndata), returndata_size) 183 | } 184 | } else { 185 | revert(errorMessage); 186 | } 187 | } 188 | } 189 | } -------------------------------------------------------------------------------- /contracts/lib/Address.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.2 <0.8.0; 4 | 5 | /** 6 | * @dev Collection of functions related to the address type 7 | */ 8 | library Address { 9 | /** 10 | * @dev Returns true if `account` is a contract. 11 | * 12 | * [IMPORTANT] 13 | * ==== 14 | * It is unsafe to assume that an address for which this function returns 15 | * false is an externally-owned account (EOA) and not a contract. 16 | * 17 | * Among others, `isContract` will return false for the following 18 | * types of addresses: 19 | * 20 | * - an externally-owned account 21 | * - a contract in construction 22 | * - an address where a contract will be created 23 | * - an address where a contract lived, but was destroyed 24 | * ==== 25 | */ 26 | function isContract(address account) internal view returns (bool) { 27 | // This method relies on extcodesize, which returns 0 for contracts in 28 | // construction, since the code is only stored at the end of the 29 | // constructor execution. 30 | 31 | uint256 size; 32 | // solhint-disable-next-line no-inline-assembly 33 | assembly { size := extcodesize(account) } 34 | return size > 0; 35 | } 36 | 37 | /** 38 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 39 | * `recipient`, forwarding all available gas and reverting on errors. 40 | * 41 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 42 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 43 | * imposed by `transfer`, making them unable to receive funds via 44 | * `transfer`. {sendValue} removes this limitation. 45 | * 46 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 47 | * 48 | * IMPORTANT: because control is transferred to `recipient`, care must be 49 | * taken to not create reentrancy vulnerabilities. Consider using 50 | * {ReentrancyGuard} or the 51 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 52 | */ 53 | function sendValue(address payable recipient, uint256 amount) internal { 54 | require(address(this).balance >= amount, "Address: insufficient balance"); 55 | 56 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 57 | (bool success, ) = recipient.call{ value: amount }(""); 58 | require(success, "Address: unable to send value, recipient may have reverted"); 59 | } 60 | 61 | /** 62 | * @dev Performs a Solidity function call using a low level `call`. A 63 | * plain`call` is an unsafe replacement for a function call: use this 64 | * function instead. 65 | * 66 | * If `target` reverts with a revert reason, it is bubbled up by this 67 | * function (like regular Solidity function calls). 68 | * 69 | * Returns the raw returned data. To convert to the expected return value, 70 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 71 | * 72 | * Requirements: 73 | * 74 | * - `target` must be a contract. 75 | * - calling `target` with `data` must not revert. 76 | * 77 | * _Available since v3.1._ 78 | */ 79 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 80 | return functionCall(target, data, "Address: low-level call failed"); 81 | } 82 | 83 | /** 84 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 85 | * `errorMessage` as a fallback revert reason when `target` reverts. 86 | * 87 | * _Available since v3.1._ 88 | */ 89 | function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 90 | return functionCallWithValue(target, data, 0, errorMessage); 91 | } 92 | 93 | /** 94 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 95 | * but also transferring `value` wei to `target`. 96 | * 97 | * Requirements: 98 | * 99 | * - the calling contract must have an ETH balance of at least `value`. 100 | * - the called Solidity function must be `payable`. 101 | * 102 | * _Available since v3.1._ 103 | */ 104 | function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { 105 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 106 | } 107 | 108 | /** 109 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 110 | * with `errorMessage` as a fallback revert reason when `target` reverts. 111 | * 112 | * _Available since v3.1._ 113 | */ 114 | function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { 115 | require(address(this).balance >= value, "Address: insufficient balance for call"); 116 | require(isContract(target), "Address: call to non-contract"); 117 | 118 | // solhint-disable-next-line avoid-low-level-calls 119 | (bool success, bytes memory returndata) = target.call{ value: value }(data); 120 | return _verifyCallResult(success, returndata, errorMessage); 121 | } 122 | 123 | /** 124 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 125 | * but performing a static call. 126 | * 127 | * _Available since v3.3._ 128 | */ 129 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 130 | return functionStaticCall(target, data, "Address: low-level static call failed"); 131 | } 132 | 133 | /** 134 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 135 | * but performing a static call. 136 | * 137 | * _Available since v3.3._ 138 | */ 139 | function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { 140 | require(isContract(target), "Address: static call to non-contract"); 141 | 142 | // solhint-disable-next-line avoid-low-level-calls 143 | (bool success, bytes memory returndata) = target.staticcall(data); 144 | return _verifyCallResult(success, returndata, errorMessage); 145 | } 146 | 147 | /** 148 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 149 | * but performing a delegate call. 150 | * 151 | * _Available since v3.3._ 152 | */ 153 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 154 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 155 | } 156 | 157 | /** 158 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 159 | * but performing a delegate call. 160 | * 161 | * _Available since v3.3._ 162 | */ 163 | function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 164 | require(isContract(target), "Address: delegate call to non-contract"); 165 | 166 | // solhint-disable-next-line avoid-low-level-calls 167 | (bool success, bytes memory returndata) = target.delegatecall(data); 168 | return _verifyCallResult(success, returndata, errorMessage); 169 | } 170 | 171 | function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { 172 | if (success) { 173 | return returndata; 174 | } else { 175 | // Look for revert reason and bubble it up if present 176 | if (returndata.length > 0) { 177 | // The easiest way to bubble the revert reason is using memory via assembly 178 | 179 | // solhint-disable-next-line no-inline-assembly 180 | assembly { 181 | let returndata_size := mload(returndata) 182 | revert(add(32, returndata), returndata_size) 183 | } 184 | } else { 185 | revert(errorMessage); 186 | } 187 | } 188 | } 189 | } -------------------------------------------------------------------------------- /contracts/lib/0.8/AccessControlUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./ContextUpgradeable.sol"; 6 | import "./ERC165Upgradeable.sol"; 7 | import "./Initializable.sol"; 8 | 9 | /** 10 | * @dev External interface of AccessControl declared to support ERC165 detection. 11 | */ 12 | interface IAccessControlUpgradeable { 13 | function hasRole(bytes32 role, address account) external view returns (bool); 14 | function getRoleAdmin(bytes32 role) external view returns (bytes32); 15 | function grantRole(bytes32 role, address account) external; 16 | function revokeRole(bytes32 role, address account) external; 17 | function renounceRole(bytes32 role, address account) external; 18 | } 19 | 20 | /** 21 | * @dev Contract module that allows children to implement role-based access 22 | * control mechanisms. This is a lightweight version that doesn't allow enumerating role 23 | * members except through off-chain means by accessing the contract event logs. Some 24 | * applications may benefit from on-chain enumerability, for those cases see 25 | * {AccessControlEnumerable}. 26 | * 27 | * Roles are referred to by their `bytes32` identifier. These should be exposed 28 | * in the external API and be unique. The best way to achieve this is by 29 | * using `public constant` hash digests: 30 | * 31 | * ``` 32 | * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); 33 | * ``` 34 | * 35 | * Roles can be used to represent a set of permissions. To restrict access to a 36 | * function call, use {hasRole}: 37 | * 38 | * ``` 39 | * function foo() public { 40 | * require(hasRole(MY_ROLE, msg.sender)); 41 | * ... 42 | * } 43 | * ``` 44 | * 45 | * Roles can be granted and revoked dynamically via the {grantRole} and 46 | * {revokeRole} functions. Each role has an associated admin role, and only 47 | * accounts that have a role's admin role can call {grantRole} and {revokeRole}. 48 | * 49 | * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means 50 | * that only accounts with this role will be able to grant or revoke other 51 | * roles. More complex role relationships can be created by using 52 | * {_setRoleAdmin}. 53 | * 54 | * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to 55 | * grant and revoke this role. Extra precautions should be taken to secure 56 | * accounts that have been granted it. 57 | */ 58 | abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable { 59 | function __AccessControl_init() internal initializer { 60 | __Context_init_unchained(); 61 | __ERC165_init_unchained(); 62 | __AccessControl_init_unchained(); 63 | } 64 | 65 | function __AccessControl_init_unchained() internal initializer { 66 | } 67 | struct RoleData { 68 | mapping (address => bool) members; 69 | bytes32 adminRole; 70 | } 71 | 72 | mapping (bytes32 => RoleData) private _roles; 73 | 74 | bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; 75 | 76 | /** 77 | * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` 78 | * 79 | * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite 80 | * {RoleAdminChanged} not being emitted signaling this. 81 | * 82 | * _Available since v3.1._ 83 | */ 84 | event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); 85 | 86 | /** 87 | * @dev Emitted when `account` is granted `role`. 88 | * 89 | * `sender` is the account that originated the contract call, an admin role 90 | * bearer except when using {_setupRole}. 91 | */ 92 | event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 93 | 94 | /** 95 | * @dev Emitted when `account` is revoked `role`. 96 | * 97 | * `sender` is the account that originated the contract call: 98 | * - if using `revokeRole`, it is the admin role bearer 99 | * - if using `renounceRole`, it is the role bearer (i.e. `account`) 100 | */ 101 | event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 102 | 103 | /** 104 | * @dev See {IERC165-supportsInterface}. 105 | */ 106 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 107 | return interfaceId == type(IAccessControlUpgradeable).interfaceId 108 | || super.supportsInterface(interfaceId); 109 | } 110 | 111 | /** 112 | * @dev Returns `true` if `account` has been granted `role`. 113 | */ 114 | function hasRole(bytes32 role, address account) public view override returns (bool) { 115 | return _roles[role].members[account]; 116 | } 117 | 118 | /** 119 | * @dev Returns the admin role that controls `role`. See {grantRole} and 120 | * {revokeRole}. 121 | * 122 | * To change a role's admin, use {_setRoleAdmin}. 123 | */ 124 | function getRoleAdmin(bytes32 role) public view override returns (bytes32) { 125 | return _roles[role].adminRole; 126 | } 127 | 128 | /** 129 | * @dev Grants `role` to `account`. 130 | * 131 | * If `account` had not been already granted `role`, emits a {RoleGranted} 132 | * event. 133 | * 134 | * Requirements: 135 | * 136 | * - the caller must have ``role``'s admin role. 137 | */ 138 | function grantRole(bytes32 role, address account) public virtual override { 139 | require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant"); 140 | 141 | _grantRole(role, account); 142 | } 143 | 144 | /** 145 | * @dev Revokes `role` from `account`. 146 | * 147 | * If `account` had been granted `role`, emits a {RoleRevoked} event. 148 | * 149 | * Requirements: 150 | * 151 | * - the caller must have ``role``'s admin role. 152 | */ 153 | function revokeRole(bytes32 role, address account) public virtual override { 154 | require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke"); 155 | 156 | _revokeRole(role, account); 157 | } 158 | 159 | /** 160 | * @dev Revokes `role` from the calling account. 161 | * 162 | * Roles are often managed via {grantRole} and {revokeRole}: this function's 163 | * purpose is to provide a mechanism for accounts to lose their privileges 164 | * if they are compromised (such as when a trusted device is misplaced). 165 | * 166 | * If the calling account had been granted `role`, emits a {RoleRevoked} 167 | * event. 168 | * 169 | * Requirements: 170 | * 171 | * - the caller must be `account`. 172 | */ 173 | function renounceRole(bytes32 role, address account) public virtual override { 174 | require(account == _msgSender(), "AccessControl: can only renounce roles for self"); 175 | 176 | _revokeRole(role, account); 177 | } 178 | 179 | /** 180 | * @dev Grants `role` to `account`. 181 | * 182 | * If `account` had not been already granted `role`, emits a {RoleGranted} 183 | * event. Note that unlike {grantRole}, this function doesn't perform any 184 | * checks on the calling account. 185 | * 186 | * [WARNING] 187 | * ==== 188 | * This function should only be called from the constructor when setting 189 | * up the initial roles for the system. 190 | * 191 | * Using this function in any other way is effectively circumventing the admin 192 | * system imposed by {AccessControl}. 193 | * ==== 194 | */ 195 | function _setupRole(bytes32 role, address account) internal virtual { 196 | _grantRole(role, account); 197 | } 198 | 199 | /** 200 | * @dev Sets `adminRole` as ``role``'s admin role. 201 | * 202 | * Emits a {RoleAdminChanged} event. 203 | */ 204 | function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { 205 | emit RoleAdminChanged(role, getRoleAdmin(role), adminRole); 206 | _roles[role].adminRole = adminRole; 207 | } 208 | 209 | function _grantRole(bytes32 role, address account) private { 210 | if (!hasRole(role, account)) { 211 | _roles[role].members[account] = true; 212 | emit RoleGranted(role, account, _msgSender()); 213 | } 214 | } 215 | 216 | function _revokeRole(bytes32 role, address account) private { 217 | if (hasRole(role, account)) { 218 | _roles[role].members[account] = false; 219 | emit RoleRevoked(role, account, _msgSender()); 220 | } 221 | } 222 | uint256[49] private __gap; 223 | } --------------------------------------------------------------------------------