├── TWAP.png ├── src ├── periphery │ ├── README.md │ ├── Taker.sol │ └── Lens.sol ├── IWETH.sol ├── IExchange.sol ├── exchange │ ├── ExchangeV2.sol │ ├── ParaswapExchange.sol │ └── P2Exchange.sol ├── OrderLib.sol └── TWAP.sol ├── Audit-Report-PeckShield.pdf ├── Audit-Report-RDAuditors.pdf ├── .gas-snapshot ├── remappings.txt ├── .gitmodules ├── foundry.toml ├── index.js ├── script ├── DeployTWAP.s.sol ├── DeployExchange.s.sol ├── DeployParaswap.s.sol ├── DeployP2Exchange.s.sol └── DeployTakers.s.sol ├── test ├── MockDeflationaryToken.sol ├── MockExchange.sol ├── Lens.t.sol ├── Taker.access.t.sol └── Taker.fill.t.sol ├── package.json ├── LICENSE ├── deploy.zsh ├── taker.abi.json ├── .gitignore ├── README.md ├── lens.abi.json ├── configs-old.json ├── configs.json ├── twap.abi.json └── TOS.md /TWAP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbs-network/twap/HEAD/TWAP.png -------------------------------------------------------------------------------- /src/periphery/README.md: -------------------------------------------------------------------------------- 1 | # Periphery 2 | 3 | These contracts are not part of the core protocol 4 | -------------------------------------------------------------------------------- /Audit-Report-PeckShield.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbs-network/twap/HEAD/Audit-Report-PeckShield.pdf -------------------------------------------------------------------------------- /Audit-Report-RDAuditors.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbs-network/twap/HEAD/Audit-Report-RDAuditors.pdf -------------------------------------------------------------------------------- /src/IWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | interface IWETH { 5 | function withdraw(uint256 wad) external; 6 | } 7 | -------------------------------------------------------------------------------- /.gas-snapshot: -------------------------------------------------------------------------------- 1 | TakerAccessTest:testRevert_NotAllowed() (gas: 18796) 2 | TakerAccessTest:test_Allowed() (gas: 21621) 3 | TakerAccessTest:test_SafeAllowanceBalance() (gas: 19624) 4 | TakerFillTest:test_erc20s() (gas: 5847) -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=lib/forge-std/lib/ds-test/src/ 2 | forge-std/=lib/forge-std/src/ 3 | @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ 4 | openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/ 5 | solmate/=lib/solmate/ 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/openzeppelin-contracts"] 5 | path = lib/openzeppelin-contracts 6 | url = https://github.com/openzeppelin/openzeppelin-contracts 7 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | solc = "0.8.16" 3 | src = "src" 4 | out = "out" 5 | libs = ["lib"] 6 | fs_permissions = [ 7 | { access = "read-write", path = ".forge-snapshots/"}, 8 | { access = "read", path = "script/input/"} 9 | ] 10 | optimizer_runs = 1_000_000 11 | verbosity = 3 12 | ignored_warnings_from = ["lib"] 13 | 14 | # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const configs = require('./configs.json'); 2 | const twapAbi = require('./twap.abi.json'); 3 | const lensAbi = require('./lens.abi.json'); 4 | const takerAbi = require('./taker.abi.json'); 5 | 6 | Object.keys(configs).forEach((key) => { 7 | if (!configs[key].twapAbi) configs[key].twapAbi = twapAbi; 8 | if (!configs[key].lensAbi) configs[key].lensAbi = lensAbi; 9 | if (!configs[key].takerAbi) configs[key].takerAbi = takerAbi; 10 | }); 11 | 12 | module.exports = { configs }; 13 | -------------------------------------------------------------------------------- /script/DeployTWAP.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "forge-std/Test.sol"; 5 | import "forge-std/Script.sol"; 6 | 7 | import {TWAP} from "src/TWAP.sol"; 8 | import {Lens} from "src/periphery/Lens.sol"; 9 | 10 | contract DeployTWAP is Script { 11 | function run() public returns (TWAP twap, Lens lens) { 12 | address weth = vm.envAddress("WETH"); 13 | 14 | vm.startBroadcast(); 15 | twap = new TWAP(weth); 16 | lens = new Lens(twap); 17 | vm.stopBroadcast(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/MockDeflationaryToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | /** 7 | * Burns 10% of every transfer 8 | */ 9 | contract MockDeflationaryToken is ERC20("MockDeflationaryToken", "MDT") { 10 | constructor() { 11 | _mint(msg.sender, 100e18); 12 | } 13 | 14 | function _afterTokenTransfer(address from, address to, uint256 amount) internal override { 15 | if (from != address(0) && to != address(0)) { 16 | _burn(to, amount / 10); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /script/DeployExchange.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "forge-std/Test.sol"; 5 | import "forge-std/Script.sol"; 6 | 7 | import {IExchange} from "src/IExchange.sol"; 8 | import {ExchangeV2} from "src/exchange/ExchangeV2.sol"; 9 | 10 | contract DeployExchange is Script { 11 | function run() public returns (IExchange) { 12 | address router = vm.envAddress("ROUTER"); 13 | address[] memory allowed = vm.envAddress("ALLOWED", ","); 14 | 15 | vm.broadcast(); 16 | return new ExchangeV2{salt: bytes32(uint256(0))}(router, allowed); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /script/DeployParaswap.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "forge-std/Test.sol"; 5 | import "forge-std/Script.sol"; 6 | 7 | import {IExchange} from "src/IExchange.sol"; 8 | import {ParaswapExchange} from "src/exchange/ParaswapExchange.sol"; 9 | 10 | contract DeployParaswap is Script { 11 | function run() public returns (IExchange) { 12 | address[] memory allowed = vm.envAddress("ALLOWED", ","); 13 | address router = vm.envAddress("ROUTER"); 14 | 15 | vm.broadcast(); 16 | return new ParaswapExchange{salt: bytes32(uint256(1))}(router, allowed); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /script/DeployP2Exchange.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "forge-std/Test.sol"; 5 | import "forge-std/Script.sol"; 6 | 7 | import {IExchange} from "src/IExchange.sol"; 8 | import {P2Exchange} from "src/exchange/P2Exchange.sol"; 9 | 10 | contract DeployP2Exchange is Script { 11 | function run() public returns (IExchange) { 12 | address router = vm.envAddress("ROUTER"); 13 | address permit2 = vm.envAddress("PERMIT2"); 14 | address[] memory allowed = vm.envAddress("ALLOWED", ","); 15 | 16 | vm.broadcast(); 17 | return new P2Exchange{salt: bytes32(uint256(0))}(permit2, router, allowed); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@orbs-network/twap", 3 | "version": "2.7.28", 4 | "repository": { 5 | "type": "git", 6 | "url": "git+https://github.com/orbs-network/twap.git" 7 | }, 8 | "author": "Orbs", 9 | "license": "MIT", 10 | "dependencies": {}, 11 | "main": "index.js", 12 | "files": [ 13 | "index.js", 14 | "configs.json", 15 | "twap.abi.json", 16 | "lens.abi.json", 17 | "taker.abi.json", 18 | "LICENSE", 19 | "TOS.md", 20 | "README.md" 21 | ], 22 | "scripts": { 23 | "clean": "forge clean", 24 | "build": "npm run clean && forge build", 25 | "prepublishOnly": "npm run build", 26 | "test": "forge test" 27 | }, 28 | "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" 29 | } 30 | -------------------------------------------------------------------------------- /src/IExchange.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "./OrderLib.sol"; 5 | 6 | /** 7 | * Adapter between TWAP and exchange implementations 8 | */ 9 | interface IExchange { 10 | /** 11 | * Returns actual output amount after fees and price impact 12 | */ 13 | function getAmountOut( 14 | address srcToken, 15 | address dstToken, 16 | uint256 amountIn, 17 | bytes calldata askData, 18 | bytes calldata bidData, 19 | address taker 20 | ) external view returns (uint256 amountOut); 21 | 22 | /** 23 | * Swaps amountIn to amount out using abi encoded data (can either be path or more complex data) 24 | */ 25 | function swap( 26 | address srcToken, 27 | address dstToken, 28 | uint256 amountIn, 29 | uint256 amountOutMin, 30 | bytes calldata askData, 31 | bytes calldata bidData, 32 | address taker 33 | ) external; 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Orbs LTD. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /deploy.zsh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | set -euo pipefail 3 | 4 | config=./configs.json 5 | 6 | trap chain EXIT 7 | 8 | export ALLOWED="0xA05405b6340A7F43dC5835351BFC4f5b1F028359,0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 9 | export ADMIN=$(a admin) 10 | 11 | # Extract unique sorted chain IDs 12 | chain_ids=($(jq -r '.[].chainId' $config | sort -n | uniq)) 13 | 14 | for chain_id in "${chain_ids[@]}"; do 15 | if [[ $chain_id -eq 324 ]]; then 16 | echo "Skipping zkSync chain (324)" 17 | continue 18 | fi 19 | 20 | # Collect all TWAP addresses for this chain_id 21 | twap_addresses=($(jq -r --argjson cid "$chain_id" ' 22 | to_entries[] | select(.value.chainId == $cid) | .value.twapAddress' $config | sort -u)) 23 | 24 | if [[ ${#twap_addresses[@]} -ne 1 ]]; then 25 | echo "Error: Multiple TWAP addresses found for chain $chain_id: ${twap_addresses[*]}" 26 | exit 1 27 | fi 28 | 29 | twap_address=${twap_addresses[1]} # zsh arrays are 1-based by default 30 | 31 | chain $chain_id 32 | autosign 33 | 34 | echo "\n🔗🔗🔗🔗🔗🔗🔗🔗🔗🔗🔗🔗 ($CHAIN_NAME $chain_id) 🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀\n" 35 | 36 | export TWAP=$twap_address 37 | 38 | echo "Deploying Takers for TWAP: $TWAP" 39 | forge script DeployTakers --broadcast 40 | done 41 | -------------------------------------------------------------------------------- /test/MockExchange.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | 7 | import "src/IExchange.sol"; 8 | 9 | contract MockExchange is IExchange { 10 | using SafeERC20 for ERC20; 11 | 12 | uint256[] public amounts; 13 | 14 | function setMockAmounts(uint256[] memory _amounts) public { 15 | amounts = _amounts; 16 | } 17 | 18 | function getAmountOut(address, address, uint256, bytes calldata, bytes calldata, address) 19 | public 20 | view 21 | returns (uint256) 22 | { 23 | return amounts[amounts.length - 1]; 24 | } 25 | 26 | /** 27 | * assumes holds balance of dstToken 28 | */ 29 | function swap( 30 | address _srcToken, 31 | address _dstToken, 32 | uint256 amountIn, 33 | uint256, 34 | bytes calldata, 35 | bytes calldata, 36 | address 37 | ) public { 38 | ERC20 srcToken = ERC20(_srcToken); 39 | ERC20 dstToken = ERC20(_dstToken); 40 | srcToken.safeTransferFrom(msg.sender, address(this), amountIn); 41 | 42 | uint256 amountOut = amounts[amounts.length - 1]; 43 | dstToken.safeTransfer(msg.sender, amountOut); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/Lens.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 7 | 8 | import {Lens, TWAP} from "src/periphery/Lens.sol"; 9 | 10 | contract TakerAccessTest is Test { 11 | TWAP public twap; 12 | Lens public lens; 13 | MockERC20 public token; 14 | address public maker = address(0x1); 15 | 16 | function setUp() public { 17 | twap = new TWAP(address(new ERC20("weth", "WETH"))); 18 | lens = new Lens(twap); 19 | token = new MockERC20(); 20 | token.transfer(maker, 100 ether); 21 | } 22 | 23 | function test_SafeAllowanceBalance() public { 24 | assertFalse(lens.hasAllowance(address(token), maker, 100)); 25 | assertFalse(lens.hasBalance(address(token), maker, 100)); 26 | vm.expectRevert(MockERC20.ThrowsError.selector); 27 | token.balanceOf(maker); 28 | } 29 | } 30 | 31 | contract MockERC20 is ERC20 { 32 | constructor() ERC20("token", "symbol") { 33 | _mint(msg.sender, 100 ether); 34 | } 35 | 36 | error ThrowsError(); 37 | 38 | function allowance(address, address) public pure override returns (uint256) { 39 | revert ThrowsError(); 40 | } 41 | 42 | function balanceOf(address) public pure override returns (uint256) { 43 | revert ThrowsError(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/Taker.access.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 7 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 8 | 9 | import {Taker, IAllowed, TWAP} from "src/periphery/Taker.sol"; 10 | 11 | contract TakerAccessTest is Test { 12 | IAllowed public admin; 13 | TWAP public twap; 14 | Taker public taker; 15 | 16 | function setUp() public { 17 | admin = new MockAdmin(); 18 | twap = new TWAP(address(new ERC20("weth", "WETH"))); 19 | taker = new Taker(); 20 | taker.init(twap, admin); 21 | } 22 | 23 | function test_Allowed() public { 24 | taker.rescue(address(0), address(this), 0); 25 | assertEq(address(taker.allowed()), address(admin)); 26 | assertEq(Ownable(address(admin)).owner(), address(this)); 27 | assertTrue(admin.allowed(address(this))); 28 | } 29 | 30 | function testRevert_NotAllowed() public { 31 | address other = makeAddr("other"); 32 | hoax(other); 33 | vm.expectRevert(abi.encodeWithSelector(Taker.NotAllowed.selector, other)); 34 | taker.rescue(address(0), address(other), 0); 35 | } 36 | 37 | receive() external payable {} 38 | } 39 | 40 | contract MockAdmin is IAllowed, Ownable { 41 | function allowed(address addr) public view returns (bool) { 42 | return addr == owner(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /script/DeployTakers.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "forge-std/Test.sol"; 5 | import "forge-std/Script.sol"; 6 | 7 | import {Taker, TWAP, IAllowed} from "src/periphery/Taker.sol"; 8 | 9 | contract DeployTakers is Script { 10 | bytes32 constant SALT0 = 0x2c727ad42be2d03d21aaf12954ec8a1b1c89f5636eaa4b6147750b7210a4b459; 11 | bytes32 constant SALT1 = 0x661183b6cf3d3c08e70cebdfbee5dcb0cd7282b29914b072c0e5e6c968529d64; 12 | 13 | function run() public returns (address taker, address taker2) { 14 | TWAP twap = TWAP(payable(vm.envAddress("TWAP"))); 15 | IAllowed admin = IAllowed(vm.envAddress("ADMIN")); 16 | taker = _taker(twap, admin, SALT0); 17 | taker2 = _taker(twap, admin, SALT1); 18 | } 19 | 20 | function _taker(TWAP twap, IAllowed admin, bytes32 salt) private returns (address taker) { 21 | bytes32 initCodeHash = hashInitCode(type(Taker).creationCode, abi.encode()); 22 | console.logBytes32(initCodeHash); 23 | taker = vm.computeCreate2Address(salt, initCodeHash); 24 | 25 | if (taker.code.length > 0) { 26 | if (Taker(payable(taker)).twap() != twap) revert("twap"); 27 | if (Taker(payable(taker)).allowed() != admin) revert("admin"); 28 | console.log("Taker already deployed"); 29 | return taker; 30 | } 31 | 32 | vm.broadcast(); 33 | Taker deployed = new Taker{salt: salt}(); 34 | if (taker != address(deployed)) revert("deployment"); 35 | 36 | vm.broadcast(); 37 | deployed.init(twap, admin); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/exchange/ExchangeV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | import "@openzeppelin/contracts/utils/Address.sol"; 6 | 7 | import "../IExchange.sol"; 8 | 9 | /** 10 | * Adapter between swap routers and TWAP's IExchange interface 11 | */ 12 | contract ExchangeV2 is IExchange { 13 | using SafeERC20 for IERC20; 14 | 15 | address public immutable router; 16 | mapping(address => bool) private allowed; 17 | 18 | error InsufficientOutputAmount(uint256 actual, uint256 minimum); 19 | error TakerNotAllowed(address taker); 20 | 21 | constructor(address _router, address[] memory _allowed) { 22 | router = _router; 23 | for (uint256 i = 0; i < _allowed.length; i++) { 24 | allowed[_allowed[i]] = true; 25 | } 26 | } 27 | 28 | function getAmountOut(address, address, uint256, bytes calldata, bytes calldata bidData, address taker) 29 | public 30 | view 31 | returns (uint256 dstAmountOut) 32 | { 33 | if (!allowed[taker]) revert TakerNotAllowed(taker); 34 | (dstAmountOut,) = decode(bidData); 35 | } 36 | 37 | function swap( 38 | address _srcToken, 39 | address _dstToken, 40 | uint256 amountIn, 41 | uint256 minOut, 42 | bytes calldata, 43 | bytes calldata bidData, 44 | address taker 45 | ) public { 46 | if (!allowed[taker]) revert TakerNotAllowed(taker); 47 | 48 | (, bytes memory swapData) = decode(bidData); 49 | IERC20 src = IERC20(_srcToken); 50 | IERC20 dst = IERC20(_dstToken); 51 | 52 | src.safeTransferFrom(msg.sender, address(this), amountIn); 53 | amountIn = src.balanceOf(address(this)); // support FoT tokens 54 | 55 | src.safeIncreaseAllowance(router, amountIn); 56 | Address.functionCall(router, swapData); 57 | 58 | uint256 balance = dst.balanceOf(address(this)); 59 | if (balance < minOut) revert InsufficientOutputAmount(balance, minOut); 60 | 61 | dst.safeTransfer(msg.sender, balance); 62 | } 63 | 64 | function decode(bytes calldata data) private pure returns (uint256 amountOut, bytes memory swapdata) { 65 | return abi.decode(data, (uint256, bytes)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/exchange/ParaswapExchange.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/utils/Address.sol"; 7 | 8 | import "../IExchange.sol"; 9 | 10 | /** 11 | * Adapter between IParaswap and TWAP's IExchange interface 12 | */ 13 | contract ParaswapExchange is IExchange { 14 | using SafeERC20 for ERC20; 15 | 16 | address public immutable paraswap; 17 | mapping(address => bool) private allowed; 18 | 19 | error InsufficientOutputAmount(uint256 actual, uint256 minimum); 20 | error TakerNotAllowed(address taker); 21 | 22 | constructor(address _router, address[] memory _allowed) { 23 | paraswap = _router; 24 | for (uint256 i = 0; i < _allowed.length; i++) { 25 | allowed[_allowed[i]] = true; 26 | } 27 | } 28 | 29 | function getAmountOut(address, address, uint256, bytes calldata, bytes calldata bidData, address taker) 30 | public 31 | view 32 | returns (uint256 dstAmountOut) 33 | { 34 | if (!allowed[taker]) revert TakerNotAllowed(taker); 35 | (dstAmountOut,) = decode(bidData); 36 | } 37 | 38 | function swap( 39 | address _srcToken, 40 | address _dstToken, 41 | uint256 amountIn, 42 | uint256 amountOutMin, 43 | bytes calldata, 44 | bytes calldata bidData, 45 | address taker 46 | ) public { 47 | if (!allowed[taker]) revert TakerNotAllowed(taker); 48 | 49 | (, bytes memory swapdata) = decode(bidData); 50 | ERC20 srcToken = ERC20(_srcToken); 51 | ERC20 dstToken = ERC20(_dstToken); 52 | 53 | srcToken.safeTransferFrom(msg.sender, address(this), amountIn); 54 | amountIn = srcToken.balanceOf(address(this)); // support FoT tokens 55 | 56 | srcToken.safeIncreaseAllowance(IParaswap(paraswap).getTokenTransferProxy(), amountIn); 57 | Address.functionCall(address(paraswap), swapdata); 58 | 59 | uint256 balance = dstToken.balanceOf(address(this)); 60 | if (balance < amountOutMin) revert InsufficientOutputAmount(balance, amountOutMin); 61 | 62 | dstToken.safeTransfer(msg.sender, balance); 63 | } 64 | 65 | function decode(bytes calldata data) private pure returns (uint256 amountOut, bytes memory swapdata) { 66 | return abi.decode(data, (uint256, bytes)); 67 | } 68 | } 69 | 70 | interface IParaswap { 71 | function getTokenTransferProxy() external view returns (address); 72 | } 73 | -------------------------------------------------------------------------------- /test/Taker.fill.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 7 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 8 | 9 | import {Taker, IAllowed, TWAP, OrderLib, IWETH} from "src/periphery/Taker.sol"; 10 | 11 | contract TakerFillTest is Test { 12 | IAllowed public admin; 13 | TWAP public twap; 14 | Taker public taker; 15 | address public swapper; 16 | 17 | address public swapFeeExchange; 18 | address public fees; 19 | bytes public swapFeeData = new bytes(0); 20 | 21 | ERC20 public srcToken; 22 | ERC20 public dstToken; 23 | IWETH public weth; 24 | 25 | uint64 public id; 26 | 27 | function setUp() public { 28 | admin = new MockAdmin(); 29 | weth = new MockWETH(vm); 30 | twap = new TWAP(address(weth)); 31 | taker = new Taker(); 32 | taker.init(twap, admin); 33 | 34 | swapper = makeAddr("swapper"); 35 | fees = makeAddr("fees"); 36 | swapFeeExchange = makeAddr("swapFeeExchange"); 37 | 38 | srcToken = new ERC20("src", "SRC"); 39 | dstToken = new ERC20("dst", "DST"); 40 | deal(address(srcToken), swapper, 100 ether); 41 | 42 | hoax(swapper); 43 | srcToken.approve(address(twap), type(uint256).max); 44 | hoax(swapper); 45 | id = twap.ask( 46 | OrderLib.Ask( 47 | address(0), 48 | address(srcToken), 49 | address(dstToken), 50 | 100 ether, 51 | 10 ether, 52 | 1, 53 | uint32(block.timestamp + 1000), 54 | 30, 55 | 0, 56 | new bytes(0) 57 | ) 58 | ); 59 | } 60 | 61 | function test_erc20s() public { 62 | vm.mockCall(address(twap), abi.encodeWithSelector(TWAP.fill.selector), new bytes(0)); 63 | 64 | // taker.fill(id, fees, 0, swapFeeExchange, swapFeeData); 65 | } 66 | 67 | receive() external payable {} 68 | } 69 | 70 | contract MockAdmin is IAllowed, Ownable { 71 | function allowed(address addr) public view returns (bool) { 72 | return addr == owner(); 73 | } 74 | } 75 | 76 | contract MockWETH is IWETH, ERC20 { 77 | Vm public vm; 78 | 79 | constructor(Vm _vm) ERC20("WETH", "WETH") { 80 | vm = _vm; 81 | } 82 | 83 | function deposit() external payable {} 84 | 85 | function withdraw(uint256 amount) external override { 86 | vm.deal(msg.sender, amount); 87 | } 88 | 89 | receive() external payable {} 90 | } 91 | -------------------------------------------------------------------------------- /src/exchange/P2Exchange.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | import "@openzeppelin/contracts/utils/Address.sol"; 6 | 7 | import "../IExchange.sol"; 8 | 9 | /** 10 | * Adapter between swap routers using Permit2 and TWAP's IExchange interface 11 | */ 12 | contract P2Exchange is IExchange { 13 | using SafeERC20 for IERC20; 14 | 15 | address public immutable permit2; 16 | address public immutable router; 17 | mapping(address => bool) private allowed; 18 | 19 | error InsufficientOutputAmount(uint256 actual, uint256 minimum); 20 | error TakerNotAllowed(address taker); 21 | 22 | constructor(address _permit2, address _router, address[] memory _allowed) { 23 | permit2 = _permit2; 24 | router = _router; 25 | for (uint256 i = 0; i < _allowed.length; i++) { 26 | allowed[_allowed[i]] = true; 27 | } 28 | } 29 | 30 | function getAmountOut(address, address, uint256, bytes calldata, bytes calldata bidData, address taker) 31 | public 32 | view 33 | returns (uint256 dstAmountOut) 34 | { 35 | if (!allowed[taker]) revert TakerNotAllowed(taker); 36 | (dstAmountOut,) = decode(bidData); 37 | } 38 | 39 | function swap( 40 | address _srcToken, 41 | address _dstToken, 42 | uint256 amountIn, 43 | uint256 minOut, 44 | bytes calldata, 45 | bytes calldata bidData, 46 | address taker 47 | ) public { 48 | if (!allowed[taker]) revert TakerNotAllowed(taker); 49 | 50 | (, bytes memory swapData) = decode(bidData); 51 | IERC20 src = IERC20(_srcToken); 52 | IERC20 dst = IERC20(_dstToken); 53 | 54 | src.safeTransferFrom(msg.sender, address(this), amountIn); 55 | amountIn = src.balanceOf(address(this)); // support FoT tokens 56 | 57 | src.forceApprove(router, amountIn); 58 | src.forceApprove(permit2, amountIn); 59 | IPermit2(permit2).approve(address(src), router, type(uint160).max, type(uint48).max); 60 | Address.functionCall(router, swapData); 61 | 62 | uint256 balance = dst.balanceOf(address(this)); 63 | if (balance < minOut) revert InsufficientOutputAmount(balance, minOut); 64 | 65 | dst.safeTransfer(msg.sender, balance); 66 | } 67 | 68 | function decode(bytes calldata data) private pure returns (uint256 amountOut, bytes memory swapdata) { 69 | return abi.decode(data, (uint256, bytes)); 70 | } 71 | } 72 | 73 | interface IPermit2 { 74 | function approve(address token, address spender, uint160 amount, uint48 expiration) external; 75 | } 76 | -------------------------------------------------------------------------------- /src/periphery/Taker.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | import {Address} from "@openzeppelin/contracts/utils/Address.sol"; 6 | 7 | import "../IExchange.sol"; 8 | import "../OrderLib.sol"; 9 | import "../TWAP.sol"; 10 | 11 | /** 12 | * Helper contract for TWAP takers 13 | * optionally swaps fee to native token at fill 14 | */ 15 | contract Taker { 16 | using SafeERC20 for ERC20; 17 | 18 | TWAP public twap; 19 | IAllowed public allowed; 20 | 21 | constructor() {} 22 | 23 | function init(TWAP _twap, IAllowed _allowed) external { 24 | if (address(twap) != address(0) || address(allowed) != address(0)) revert(); 25 | twap = _twap; 26 | allowed = _allowed; 27 | } 28 | 29 | function bid(uint64 id, address exchange, uint256 dstFee, uint32 slippagePercent, bytes calldata data) 30 | external 31 | onlyAllowed 32 | { 33 | twap.bid(id, exchange, dstFee, slippagePercent, data); 34 | } 35 | 36 | function fill(uint64 id, address fee, uint256 dstSenderAmount, address feeSwapExchange, bytes calldata feeSwapData) 37 | external 38 | onlyAllowed 39 | { 40 | // fill 41 | twap.fill(id); 42 | OrderLib.Order memory o = twap.order(id); 43 | 44 | // swap to gas 45 | bool swapGas = feeSwapExchange != address(0) && o.ask.dstToken != twap.iweth() && o.ask.dstToken != address(0); 46 | if (swapGas) { 47 | ERC20(o.ask.dstToken).safeApprove(feeSwapExchange, dstSenderAmount); 48 | IExchange(feeSwapExchange).swap( 49 | o.ask.dstToken, twap.iweth(), dstSenderAmount, 0, o.ask.data, feeSwapData, address(this) 50 | ); 51 | } 52 | 53 | // unwrap 54 | uint256 wethBalance = ERC20(twap.iweth()).balanceOf(address(this)); 55 | if (wethBalance > 0) IWETH(twap.iweth()).withdraw(wethBalance); 56 | 57 | // gas 58 | rescue(address(0), msg.sender, swapGas ? 0 : dstSenderAmount); 59 | 60 | // fee 61 | rescue(address(0), fee, 0); 62 | rescue(o.ask.dstToken, fee, 0); 63 | } 64 | 65 | function rescue(address token, address to, uint256 amount) public onlyAllowed { 66 | if (token != address(0)) { 67 | amount = amount != 0 ? amount : ERC20(token).balanceOf(address(this)); 68 | if (amount != 0) ERC20(token).safeTransfer(to, amount); 69 | } else { 70 | amount = amount != 0 ? amount : address(this).balance; 71 | if (amount != 0) Address.sendValue(payable(to), amount); 72 | } 73 | } 74 | 75 | receive() external payable {} // solhint-disable-line no-empty-blocks 76 | 77 | error NotAllowed(address caller); 78 | 79 | modifier onlyAllowed() { 80 | if (!allowed.allowed(msg.sender)) revert NotAllowed(msg.sender); 81 | _; 82 | } 83 | } 84 | 85 | interface IAllowed { 86 | function allowed(address) external view returns (bool); 87 | } 88 | -------------------------------------------------------------------------------- /src/OrderLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.x; 3 | 4 | import "@openzeppelin/contracts/utils/math/Math.sol"; 5 | 6 | library OrderLib { 7 | struct Order { 8 | uint64 id; // order id 9 | uint32 status; // status: deadline, canceled or completed 10 | uint32 time; // order creation timestamp 11 | uint32 filledTime; // last fill timestamp 12 | uint256 srcFilledAmount; // srcToken total filled amount 13 | address maker; // order creator 14 | Ask ask; // order ask parameters 15 | Bid bid; // current winning bid 16 | } 17 | 18 | struct Ask { 19 | address exchange; // restirct swap to this exchange, or zero address for any exchange 20 | address srcToken; // input token 21 | address dstToken; // output token 22 | uint256 srcAmount; // input total order amount 23 | uint256 srcBidAmount; // input chunk size 24 | uint256 dstMinAmount; // minimum output chunk size 25 | uint32 deadline; // order duration timestamp 26 | uint32 bidDelay; // minimum delay in seconds before a bid can be filled 27 | uint32 fillDelay; // minimum delay in seconds between chunks 28 | bytes data; // optional swap data for exchange 29 | } 30 | 31 | struct Bid { 32 | uint32 time; // bid creation timestamp 33 | address taker; // bidder 34 | address exchange; // execute bid on this exchange, never zero 35 | uint256 dstAmount; // dstToken actual output amount for this bid after exchange fees, taker fee and slippage 36 | uint256 dstFee; // dstToken requested by taker for performing the bid and fill 37 | bytes data; // optional additional swap data for exchange 38 | } 39 | 40 | /** 41 | * new Order for msg.sender 42 | */ 43 | function newOrder(uint64 id, Ask calldata ask) internal view returns (Order memory) { 44 | require( 45 | block.timestamp < type(uint32).max && ask.deadline < type(uint32).max && ask.bidDelay < type(uint32).max 46 | && ask.fillDelay < type(uint32).max, 47 | "uint32" 48 | ); 49 | return Order( 50 | id, 51 | ask.deadline, // status 52 | uint32(block.timestamp), // time 53 | 0, // filledTime 54 | 0, // srcFilledAmount 55 | msg.sender, // maker 56 | ask, 57 | Bid( 58 | 0, // time 59 | address(0), // taker 60 | address(0), // exchange 61 | 0, // dstAmount 62 | 0, // dstFee 63 | new bytes(0) // data 64 | ) 65 | ); 66 | } 67 | 68 | /** 69 | * new Bid 70 | */ 71 | function newBid(Order memory self, address exchange, uint256 dstAmountOut, uint256 dstFee, bytes memory data) 72 | internal 73 | view 74 | { 75 | require(block.timestamp < type(uint32).max, "uint32"); 76 | self.bid = OrderLib.Bid(uint32(block.timestamp), msg.sender, exchange, dstAmountOut, dstFee, data); 77 | } 78 | 79 | /** 80 | * chunk filled 81 | */ 82 | function filled(Order memory self, uint256 srcAmountIn) internal view { 83 | require(block.timestamp < type(uint32).max, "uint32"); 84 | delete self.bid; 85 | self.filledTime = uint32(block.timestamp); 86 | self.srcFilledAmount += srcAmountIn; 87 | } 88 | 89 | /** 90 | * next chunk srcToken: either ask.srcBidAmount or leftover 91 | */ 92 | function srcBidAmountNext(Order memory self) internal pure returns (uint256) { 93 | return Math.min(self.ask.srcBidAmount, self.ask.srcAmount - self.srcFilledAmount); 94 | } 95 | 96 | /** 97 | * next chunk dstToken minimum amount out 98 | */ 99 | function dstMinAmountNext(Order memory self) internal pure returns (uint256) { 100 | return (self.ask.dstMinAmount * srcBidAmountNext(self)) / self.ask.srcBidAmount; 101 | } 102 | 103 | /** 104 | * next chunk expected output in dstToken, or winning bid, to be sent to maker (after fees) 105 | */ 106 | function dstExpectedOutNext(Order memory self) internal pure returns (uint256) { 107 | return Math.max(self.bid.dstAmount, dstMinAmountNext(self)); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /taker.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "constructor", 4 | "inputs": [], 5 | "stateMutability": "nonpayable" 6 | }, 7 | { 8 | "type": "receive", 9 | "stateMutability": "payable" 10 | }, 11 | { 12 | "type": "function", 13 | "name": "allowed", 14 | "inputs": [], 15 | "outputs": [ 16 | { 17 | "name": "", 18 | "type": "address", 19 | "internalType": "contract IAllowed" 20 | } 21 | ], 22 | "stateMutability": "view" 23 | }, 24 | { 25 | "type": "function", 26 | "name": "bid", 27 | "inputs": [ 28 | { 29 | "name": "id", 30 | "type": "uint64", 31 | "internalType": "uint64" 32 | }, 33 | { 34 | "name": "exchange", 35 | "type": "address", 36 | "internalType": "address" 37 | }, 38 | { 39 | "name": "dstFee", 40 | "type": "uint256", 41 | "internalType": "uint256" 42 | }, 43 | { 44 | "name": "slippagePercent", 45 | "type": "uint32", 46 | "internalType": "uint32" 47 | }, 48 | { 49 | "name": "data", 50 | "type": "bytes", 51 | "internalType": "bytes" 52 | } 53 | ], 54 | "outputs": [], 55 | "stateMutability": "nonpayable" 56 | }, 57 | { 58 | "type": "function", 59 | "name": "fill", 60 | "inputs": [ 61 | { 62 | "name": "id", 63 | "type": "uint64", 64 | "internalType": "uint64" 65 | }, 66 | { 67 | "name": "fee", 68 | "type": "address", 69 | "internalType": "address" 70 | }, 71 | { 72 | "name": "dstSenderAmount", 73 | "type": "uint256", 74 | "internalType": "uint256" 75 | }, 76 | { 77 | "name": "feeSwapExchange", 78 | "type": "address", 79 | "internalType": "address" 80 | }, 81 | { 82 | "name": "feeSwapData", 83 | "type": "bytes", 84 | "internalType": "bytes" 85 | } 86 | ], 87 | "outputs": [], 88 | "stateMutability": "nonpayable" 89 | }, 90 | { 91 | "type": "function", 92 | "name": "init", 93 | "inputs": [ 94 | { 95 | "name": "_twap", 96 | "type": "address", 97 | "internalType": "contract TWAP" 98 | }, 99 | { 100 | "name": "_allowed", 101 | "type": "address", 102 | "internalType": "contract IAllowed" 103 | } 104 | ], 105 | "outputs": [], 106 | "stateMutability": "nonpayable" 107 | }, 108 | { 109 | "type": "function", 110 | "name": "rescue", 111 | "inputs": [ 112 | { 113 | "name": "token", 114 | "type": "address", 115 | "internalType": "address" 116 | }, 117 | { 118 | "name": "to", 119 | "type": "address", 120 | "internalType": "address" 121 | }, 122 | { 123 | "name": "amount", 124 | "type": "uint256", 125 | "internalType": "uint256" 126 | } 127 | ], 128 | "outputs": [], 129 | "stateMutability": "nonpayable" 130 | }, 131 | { 132 | "type": "function", 133 | "name": "twap", 134 | "inputs": [], 135 | "outputs": [ 136 | { 137 | "name": "", 138 | "type": "address", 139 | "internalType": "contract TWAP" 140 | } 141 | ], 142 | "stateMutability": "view" 143 | }, 144 | { 145 | "type": "error", 146 | "name": "NotAllowed", 147 | "inputs": [ 148 | { 149 | "name": "caller", 150 | "type": "address", 151 | "internalType": "address" 152 | } 153 | ] 154 | } 155 | ] 156 | -------------------------------------------------------------------------------- /src/periphery/Lens.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import "@openzeppelin/contracts/utils/math/Math.sol"; 5 | import "@openzeppelin/contracts/utils/Address.sol"; 6 | 7 | import "../OrderLib.sol"; 8 | import "../TWAP.sol"; 9 | 10 | /** 11 | * Helper contract to allow for efficient paginated filtered reads of Orders, instead of relying on events 12 | */ 13 | contract Lens { 14 | using OrderLib for OrderLib.Order; 15 | 16 | TWAP public immutable twap; 17 | 18 | constructor(TWAP _twap) { 19 | twap = _twap; 20 | } 21 | 22 | function length() public view returns (uint64) { 23 | return twap.length(); 24 | } 25 | 26 | function makerOrders(address maker) external view returns (OrderLib.Order[] memory result) { 27 | uint64[] memory orderIds = twap.orderIdsByMaker(maker); 28 | 29 | result = new OrderLib.Order[](orderIds.length); 30 | for (uint64 i = 0; i < result.length; i++) { 31 | uint64 id = orderIds[i]; 32 | result[i] = twap.order(id); 33 | delete result[i].bid.data; 34 | } 35 | } 36 | 37 | /** 38 | * returns all orders waiting to be bid on now by the taker, paginated 39 | * taker: desired taker 40 | * lastIndex: last order id, start with length-1 41 | * pageSize: size of iteration restricted by block gas limit. 2500 is measured to be < 15m gas 42 | */ 43 | function takerBiddableOrders(address taker, uint64 lastIndex, uint64 pageSize) 44 | external 45 | view 46 | returns (OrderLib.Order[] memory result) 47 | { 48 | OrderLib.Order[] memory orders = paginated(lastIndex, pageSize); 49 | uint64 count = 0; 50 | 51 | for (uint64 i = 0; i < orders.length; i++) { 52 | uint64 id = lastIndex - i; 53 | if (block.timestamp < twap.status(id)) { 54 | OrderLib.Order memory o = twap.order(id); 55 | if ( 56 | block.timestamp > o.filledTime + o.ask.fillDelay // after fill delay 57 | && (o.bid.taker != taker || block.timestamp > o.bid.time + twap.STALE_BID_SECONDS()) // other taker or stale bid 58 | && hasAllowance(o.ask.srcToken, o.maker, o.srcBidAmountNext()) // maker allowance 59 | && hasBalance(o.ask.srcToken, o.maker, o.srcBidAmountNext()) // maker balance 60 | ) { 61 | delete o.bid.data; 62 | orders[count] = o; 63 | count++; 64 | } 65 | } 66 | } 67 | 68 | result = new OrderLib.Order[](count); 69 | for (uint64 i = 0; i < count; i++) { 70 | result[i] = orders[i]; 71 | } 72 | } 73 | 74 | /** 75 | * returns all orders waiting to be filled now by the taker, paginated 76 | * taker: desired taker 77 | * lastIndex: last order id, start with length-1 78 | * pageSize: size of iteration restricted by block gas limit. 2500 is measured to be < 15m gas 79 | */ 80 | function takerFillableOrders(address taker, uint64 lastIndex, uint64 pageSize) 81 | external 82 | view 83 | returns (OrderLib.Order[] memory result) 84 | { 85 | OrderLib.Order[] memory orders = paginated(lastIndex, pageSize); 86 | uint64 count = 0; 87 | 88 | for (uint64 i = 0; i < orders.length; i++) { 89 | uint64 id = lastIndex - i; 90 | if (block.timestamp < twap.status(id)) { 91 | OrderLib.Order memory o = twap.order(id); 92 | if ( 93 | o.bid.taker == taker // winning taker 94 | && block.timestamp > o.bid.time + o.ask.bidDelay // after bid delay 95 | && hasAllowance(o.ask.srcToken, o.maker, o.srcBidAmountNext()) // maker allowance 96 | && hasBalance(o.ask.srcToken, o.maker, o.srcBidAmountNext()) // maker balance 97 | ) { 98 | delete o.bid.data; 99 | orders[count] = o; 100 | count++; 101 | } 102 | } 103 | } 104 | 105 | result = new OrderLib.Order[](count); 106 | for (uint64 i = 0; i < count; i++) { 107 | result[i] = orders[i]; 108 | } 109 | } 110 | 111 | function paginated(uint64 lastIndex, uint64 pageSize) private view returns (OrderLib.Order[] memory) { 112 | require(lastIndex < length(), "lastIndex"); 113 | return new OrderLib.Order[](Math.min(lastIndex + 1, pageSize)); 114 | } 115 | 116 | function hasAllowance(address token, address maker, uint256 srcBidAmountNext) public view returns (bool) { 117 | try ERC20(token).allowance(maker, address(twap)) returns (uint256 allowance) { 118 | return allowance >= srcBidAmountNext; 119 | } catch { 120 | return false; 121 | } 122 | } 123 | 124 | function hasBalance(address token, address maker, uint256 srcBidAmountNext) public view returns (bool) { 125 | try ERC20(token).balanceOf(maker) returns (uint256 balance) { 126 | return balance >= srcBidAmountNext; 127 | } catch { 128 | return false; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zkout/ 2 | .env 3 | .idea 4 | artifacts/ 5 | cache/ 6 | broadcast/ 7 | 8 | 9 | # Created by https://www.toptal.com/developers/gitignore/api/zsh,node,macos,intellij+all,solidity,soliditytruffle,sublimetext 10 | # Edit at https://www.toptal.com/developers/gitignore?templates=zsh,node,macos,intellij+all,solidity,soliditytruffle,sublimetext 11 | 12 | ### Intellij+all ### 13 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 14 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 15 | 16 | # User-specific stuff 17 | .idea/**/workspace.xml 18 | .idea/**/tasks.xml 19 | .idea/**/usage.statistics.xml 20 | .idea/**/dictionaries 21 | .idea/**/shelf 22 | 23 | # AWS User-specific 24 | .idea/**/aws.xml 25 | 26 | # Generated files 27 | .idea/**/contentModel.xml 28 | 29 | # Sensitive or high-churn files 30 | .idea/**/dataSources/ 31 | .idea/**/dataSources.ids 32 | .idea/**/dataSources.local.xml 33 | .idea/**/sqlDataSources.xml 34 | .idea/**/dynamic.xml 35 | .idea/**/uiDesigner.xml 36 | .idea/**/dbnavigator.xml 37 | 38 | # Gradle 39 | .idea/**/gradle.xml 40 | .idea/**/libraries 41 | 42 | # Gradle and Maven with auto-import 43 | # When using Gradle or Maven with auto-import, you should exclude module files, 44 | # since they will be recreated, and may cause churn. Uncomment if using 45 | # auto-import. 46 | # .idea/artifacts 47 | # .idea/compiler.xml 48 | # .idea/jarRepositories.xml 49 | # .idea/modules.xml 50 | # .idea/*.iml 51 | # .idea/modules 52 | # *.iml 53 | # *.ipr 54 | 55 | # CMake 56 | cmake-build-*/ 57 | 58 | # Mongo Explorer plugin 59 | .idea/**/mongoSettings.xml 60 | 61 | # File-based project format 62 | *.iws 63 | 64 | # IntelliJ 65 | out/ 66 | 67 | # mpeltonen/sbt-idea plugin 68 | .idea_modules/ 69 | 70 | # JIRA plugin 71 | atlassian-ide-plugin.xml 72 | 73 | # Cursive Clojure plugin 74 | .idea/replstate.xml 75 | 76 | # SonarLint plugin 77 | .idea/sonarlint/ 78 | 79 | # Crashlytics plugin (for Android Studio and IntelliJ) 80 | com_crashlytics_export_strings.xml 81 | crashlytics.properties 82 | crashlytics-build.properties 83 | fabric.properties 84 | 85 | # Editor-based Rest Client 86 | .idea/httpRequests 87 | 88 | # Android studio 3.1+ serialized cache file 89 | .idea/caches/build_file_checksums.ser 90 | 91 | ### Intellij+all Patch ### 92 | # Ignore everything but code style settings and run configurations 93 | # that are supposed to be shared within teams. 94 | 95 | .idea/* 96 | 97 | !.idea/codeStyles 98 | !.idea/runConfigurations 99 | 100 | ### macOS ### 101 | # General 102 | .DS_Store 103 | .AppleDouble 104 | .LSOverride 105 | 106 | # Icon must end with two \r 107 | Icon 108 | 109 | 110 | # Thumbnails 111 | ._* 112 | 113 | # Files that might appear in the root of a volume 114 | .DocumentRevisions-V100 115 | .fseventsd 116 | .Spotlight-V100 117 | .TemporaryItems 118 | .Trashes 119 | .VolumeIcon.icns 120 | .com.apple.timemachine.donotpresent 121 | 122 | # Directories potentially created on remote AFP share 123 | .AppleDB 124 | .AppleDesktop 125 | Network Trash Folder 126 | Temporary Items 127 | .apdisk 128 | 129 | ### macOS Patch ### 130 | # iCloud generated files 131 | *.icloud 132 | 133 | ### Node ### 134 | # Logs 135 | logs 136 | *.log 137 | npm-debug.log* 138 | yarn-debug.log* 139 | yarn-error.log* 140 | lerna-debug.log* 141 | .pnpm-debug.log* 142 | 143 | # Diagnostic reports (https://nodejs.org/api/report.html) 144 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 145 | 146 | # Runtime data 147 | pids 148 | *.pid 149 | *.seed 150 | *.pid.lock 151 | 152 | # Directory for instrumented libs generated by jscoverage/JSCover 153 | lib-cov 154 | 155 | # Coverage directory used by tools like istanbul 156 | coverage 157 | *.lcov 158 | 159 | # nyc test coverage 160 | .nyc_output 161 | 162 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 163 | .grunt 164 | 165 | # Bower dependency directory (https://bower.io/) 166 | bower_components 167 | 168 | # node-waf configuration 169 | .lock-wscript 170 | 171 | # Compiled binary addons (https://nodejs.org/api/addons.html) 172 | build/Release 173 | 174 | # Dependency directories 175 | node_modules/ 176 | jspm_packages/ 177 | 178 | # Snowpack dependency directory (https://snowpack.dev/) 179 | web_modules/ 180 | 181 | # TypeScript cache 182 | *.tsbuildinfo 183 | 184 | # Optional npm cache directory 185 | .npm 186 | 187 | # Optional eslint cache 188 | .eslintcache 189 | 190 | # Optional stylelint cache 191 | .stylelintcache 192 | 193 | # Microbundle cache 194 | .rpt2_cache/ 195 | .rts2_cache_cjs/ 196 | .rts2_cache_es/ 197 | .rts2_cache_umd/ 198 | 199 | # Optional REPL history 200 | .node_repl_history 201 | 202 | # Output of 'npm pack' 203 | *.tgz 204 | 205 | # Yarn Integrity file 206 | .yarn-integrity 207 | 208 | # dotenv environment variable files 209 | .env 210 | .env.development.local 211 | .env.test.local 212 | .env.production.local 213 | .env.local 214 | 215 | # parcel-bundler cache (https://parceljs.org/) 216 | .cache 217 | .parcel-cache 218 | 219 | # Next.js build output 220 | .next 221 | out 222 | 223 | # Nuxt.js build / generate output 224 | .nuxt 225 | dist 226 | 227 | # Gatsby files 228 | .cache/ 229 | # Comment in the public line in if your project uses Gatsby and not Next.js 230 | # https://nextjs.org/blog/next-9-1#public-directory-support 231 | # public 232 | 233 | # vuepress build output 234 | .vuepress/dist 235 | 236 | # vuepress v2.x temp and cache directory 237 | .temp 238 | 239 | # Docusaurus cache and generated files 240 | .docusaurus 241 | 242 | # Serverless directories 243 | .serverless/ 244 | 245 | # FuseBox cache 246 | .fusebox/ 247 | 248 | # DynamoDB Local files 249 | .dynamodb/ 250 | 251 | # TernJS port file 252 | .tern-port 253 | 254 | # Stores VSCode versions used for testing VSCode extensions 255 | .vscode-test 256 | 257 | # yarn v2 258 | .yarn/cache 259 | .yarn/unplugged 260 | .yarn/build-state.yml 261 | .yarn/install-state.gz 262 | .pnp.* 263 | 264 | ### Node Patch ### 265 | # Serverless Webpack directories 266 | .webpack/ 267 | 268 | # Optional stylelint cache 269 | 270 | # SvelteKit build / generate output 271 | .svelte-kit 272 | 273 | ### Solidity ### 274 | # Logs 275 | 276 | # Diagnostic reports (https://nodejs.org/api/report.html) 277 | 278 | # Runtime data 279 | 280 | # Directory for instrumented libs generated by jscoverage/JSCover 281 | 282 | # Coverage directory used by tools like istanbul 283 | 284 | # nyc test coverage 285 | 286 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 287 | 288 | # Bower dependency directory (https://bower.io/) 289 | 290 | # node-waf configuration 291 | 292 | # Compiled binary addons (https://nodejs.org/api/addons.html) 293 | 294 | # Dependency directories 295 | 296 | # Snowpack dependency directory (https://snowpack.dev/) 297 | 298 | # TypeScript cache 299 | 300 | # Optional npm cache directory 301 | 302 | # Optional eslint cache 303 | 304 | # Optional stylelint cache 305 | 306 | # Microbundle cache 307 | 308 | # Optional REPL history 309 | 310 | # Output of 'npm pack' 311 | 312 | # Yarn Integrity file 313 | 314 | # dotenv environment variable files 315 | 316 | # parcel-bundler cache (https://parceljs.org/) 317 | 318 | # Next.js build output 319 | 320 | # Nuxt.js build / generate output 321 | 322 | # Gatsby files 323 | # Comment in the public line in if your project uses Gatsby and not Next.js 324 | # https://nextjs.org/blog/next-9-1#public-directory-support 325 | # public 326 | 327 | # vuepress build output 328 | 329 | # vuepress v2.x temp and cache directory 330 | 331 | # Docusaurus cache and generated files 332 | 333 | # Serverless directories 334 | 335 | # FuseBox cache 336 | 337 | # DynamoDB Local files 338 | 339 | # TernJS port file 340 | 341 | # Stores VSCode versions used for testing VSCode extensions 342 | 343 | # yarn v2 344 | 345 | ### SolidityTruffle ### 346 | # depedencies 347 | node_modules 348 | 349 | # testing 350 | 351 | # production 352 | build 353 | build_webpack 354 | 355 | # misc 356 | npm-debug.log 357 | .truffle-solidity-loader 358 | .vagrant/** 359 | blockchain/geth/** 360 | blockchain/keystore/** 361 | blockchain/history 362 | 363 | #truffle 364 | yarn.lock 365 | package-lock.json 366 | 367 | ### SublimeText ### 368 | # Cache files for Sublime Text 369 | *.tmlanguage.cache 370 | *.tmPreferences.cache 371 | *.stTheme.cache 372 | 373 | # Workspace files are user-specific 374 | *.sublime-workspace 375 | 376 | # Project files should be checked into the repository, unless a significant 377 | # proportion of contributors will probably not be using Sublime Text 378 | # *.sublime-project 379 | 380 | # SFTP configuration file 381 | sftp-config.json 382 | sftp-config-alt*.json 383 | 384 | # Package control specific files 385 | Package Control.last-run 386 | Package Control.ca-list 387 | Package Control.ca-bundle 388 | Package Control.system-ca-bundle 389 | Package Control.cache/ 390 | Package Control.ca-certs/ 391 | Package Control.merged-ca-bundle 392 | Package Control.user-ca-bundle 393 | oscrypto-ca-bundle.crt 394 | bh_unicode_properties.cache 395 | 396 | # Sublime-github package stores a github token in this file 397 | # https://packagecontrol.io/packages/sublime-github 398 | GitHub.sublime-settings 399 | 400 | ### Zsh ### 401 | # Zsh compiled script + zrecompile backup 402 | *.zwc 403 | *.zwc.old 404 | 405 | # Zsh completion-optimization dumpfile 406 | *zcompdump* 407 | 408 | # Zsh zcalc history 409 | .zcalc_history 410 | 411 | # A popular plugin manager's files 412 | ._zinit 413 | .zinit_lstupd 414 | 415 | # zdharma/zshelldoc tool's files 416 | zsdoc/data 417 | 418 | # robbyrussell/oh-my-zsh/plugins/per-directory-history plugin's files 419 | # (when set-up to store the history in the local directory) 420 | .directory_history 421 | 422 | # MichaelAquilina/zsh-autoswitch-virtualenv plugin's files 423 | # (for Zsh plugins using Python) 424 | .venv 425 | 426 | # Zunit tests' output 427 | /tests/_output/* 428 | !/tests/_output/.gitkeep 429 | 430 | # End of https://www.toptal.com/developers/gitignore/api/zsh,node,macos,intellij+all,solidity,soliditytruffle,sublimetext 431 | .aider* 432 | -------------------------------------------------------------------------------- /src/TWAP.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/utils/Address.sol"; 7 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 8 | 9 | import "./OrderLib.sol"; 10 | import "./IExchange.sol"; 11 | import "./IWETH.sol"; 12 | 13 | /** 14 | * --------------------------- 15 | * Time-Weighted Average Price 16 | * --------------------------- 17 | * 18 | * https://github.com/orbs-network/twap 19 | * 20 | * This smart contract allows the incentivized execution of a TWAP order (either a Limit Order or a Market Order) on any DEX, with the possibility of partial fills. 21 | * 22 | * A TWAP order breaks a larger order down into smaller trades or "chunks", which are executed over a set period of time. 23 | * This is a common strategy in traditional finance but it was not previously possible to execute such trades in a decentralized manner in DeFi systems. 24 | * 25 | * In this smart contract, users (makers) create orders that wait in the contract to be filled. Once made, these orders enable an English Auction bidding war on each chunk at its time interval. 26 | * Anyone willing to participate can serve as a “taker” by finding the best path to fill the order for the next chunk on any DEX, 27 | * within the parameters set by the maker. Takers submit these paths as a bid to the contract, which selects the winner based on criteria described in detail below. 28 | * 29 | * The winning taker receives a portion of the output tokens as a reward for their effort. 30 | * 31 | * One honest taker (i.e., a taker who is willing to set the fee at the minimum amount needed to cover gas costs) 32 | * is enough to ensure the entire system functions effectively at spot prices. 33 | * 34 | * The contract is set to operate only up to the year 2106 (32bit timestamps), at which point it will no longer be usable. 35 | * 36 | * The TWAP Smart Contract does not hold any funds, has no owners, administrators, or other roles and is entirely immutable once deployed on an EVM blockchain. 37 | * 38 | */ 39 | contract TWAP is ReentrancyGuard { 40 | using SafeERC20 for ERC20; 41 | using Address for address; 42 | using OrderLib for OrderLib.Order; 43 | 44 | uint8 public constant VERSION = 4; 45 | 46 | event OrderCreated(uint64 indexed id, address indexed maker, address indexed exchange, OrderLib.Ask ask); 47 | event OrderBid( 48 | uint64 indexed id, address indexed maker, address indexed exchange, uint32 slippagePercent, OrderLib.Bid bid 49 | ); 50 | event OrderFilled( 51 | uint64 indexed id, 52 | address indexed maker, 53 | address indexed exchange, 54 | address taker, 55 | uint256 srcAmountIn, 56 | uint256 dstAmountOut, 57 | uint256 dstFee, 58 | uint256 srcFilledAmount 59 | ); 60 | event OrderCompleted(uint64 indexed id, address indexed maker, address indexed exchange, address taker); 61 | event OrderCanceled(uint64 indexed id, address indexed maker, address sender); 62 | 63 | uint32 public constant PERCENT_BASE = 100_000; 64 | uint32 public constant MIN_OUTBID_PERCENT = 101_000; 65 | uint32 public constant STALE_BID_SECONDS = 60 * 10; 66 | uint32 public constant MIN_BID_DELAY_SECONDS = 30; 67 | 68 | uint32 public constant STATUS_CANCELED = 1; 69 | uint32 public constant STATUS_COMPLETED = 2; 70 | 71 | OrderLib.Order[] public book; 72 | uint32[] public status; // STATUS or deadline timestamp by order id, used for gas efficient order filtering 73 | mapping(address => uint64[]) public makerOrders; 74 | 75 | address public immutable iweth; 76 | 77 | constructor(address _iweth) { 78 | iweth = _iweth; 79 | } 80 | 81 | // -------- views -------- 82 | 83 | /** 84 | * returns Order by order id 85 | */ 86 | function order(uint64 id) public view returns (OrderLib.Order memory) { 87 | require(id < length(), "invalid id"); 88 | return book[id]; 89 | } 90 | 91 | /** 92 | * returns order book length 93 | */ 94 | function length() public view returns (uint64) { 95 | return uint64(book.length); 96 | } 97 | 98 | function orderIdsByMaker(address maker) external view returns (uint64[] memory) { 99 | return makerOrders[maker]; 100 | } 101 | 102 | // -------- actions -------- 103 | 104 | /** 105 | * Create Order by msg.sender (maker) 106 | * 107 | * returns order id, emits OrderCreated 108 | */ 109 | function ask(OrderLib.Ask calldata _ask) external nonReentrant returns (uint64 id) { 110 | require( 111 | _ask.srcToken != address(0) && _ask.srcToken != _ask.dstToken 112 | && (_ask.srcToken != iweth || _ask.dstToken != address(0)) && _ask.srcAmount > 0 && _ask.srcBidAmount > 0 113 | && _ask.srcBidAmount <= _ask.srcAmount && _ask.dstMinAmount > 0 && _ask.deadline > block.timestamp 114 | && _ask.bidDelay >= MIN_BID_DELAY_SECONDS, 115 | "params" 116 | ); 117 | 118 | OrderLib.Order memory o = OrderLib.newOrder(length(), _ask); 119 | verifyMakerBalance(o); 120 | 121 | book.push(o); 122 | status.push(o.status); 123 | makerOrders[msg.sender].push(o.id); 124 | emit OrderCreated(o.id, o.maker, o.ask.exchange, o.ask); 125 | return o.id; 126 | } 127 | 128 | /** 129 | * Bid for a specific order by id (msg.sender is taker) 130 | * A valid bid is higher than current bid, with sufficient price after fees and after last fill delay. Invalid bids are reverted. 131 | * id: order id 132 | * exchange: bid to swap on exchange 133 | * dstFee: fee to traker in dstToken, taken from the swapped amount 134 | * slippagePercent: price output difference tolerance percent / 100,000. 0 means no slippage 135 | * data: swap data to pass to the exchange, for example the route path 136 | * emits OrderBid event 137 | */ 138 | function bid(uint64 id, address exchange, uint256 dstFee, uint32 slippagePercent, bytes calldata data) 139 | external 140 | nonReentrant 141 | { 142 | require(exchange != address(0) && slippagePercent < PERCENT_BASE, "params"); 143 | OrderLib.Order memory o = order(id); 144 | uint256 dstAmountOut = verifyBid(o, exchange, dstFee, slippagePercent, data); 145 | o.newBid(exchange, dstAmountOut, dstFee, data); 146 | book[id] = o; 147 | emit OrderBid(o.id, o.maker, exchange, slippagePercent, o.bid); 148 | } 149 | 150 | /** 151 | * Fill the current winning bid by the winning taker, if after the bidding window. Invalid fills are reverted. 152 | * id: order id 153 | * emits OrderFilled 154 | * if order is fully filled emits OrderCompleted and status is updated 155 | */ 156 | function fill(uint64 id) external nonReentrant { 157 | OrderLib.Order memory o = order(id); 158 | 159 | (address exchange, uint256 srcAmountIn, uint256 dstAmountOut, uint256 dstFee) = performFill(o); 160 | o.filled(srcAmountIn); 161 | 162 | emit OrderFilled(id, o.maker, exchange, msg.sender, srcAmountIn, dstAmountOut, dstFee, o.srcFilledAmount); 163 | 164 | if (o.srcBidAmountNext() == 0) { 165 | status[id] = STATUS_COMPLETED; 166 | o.status = STATUS_COMPLETED; 167 | emit OrderCompleted(o.id, o.maker, exchange, msg.sender); 168 | } 169 | book[id] = o; 170 | } 171 | 172 | /** 173 | * Cancel order by id, only callable by maker 174 | * id: order id 175 | * emits OrderCanceled 176 | */ 177 | function cancel(uint64 id) external nonReentrant { 178 | OrderLib.Order memory o = order(id); 179 | require(msg.sender == o.maker, "maker"); 180 | status[id] = STATUS_CANCELED; 181 | o.status = STATUS_CANCELED; 182 | book[id] = o; 183 | emit OrderCanceled(o.id, o.maker, msg.sender); 184 | } 185 | 186 | /** 187 | * Called by anyone to mark a stale invalid order as canceled 188 | * id: order id 189 | * emits OrderCanceled 190 | */ 191 | function prune(uint64 id) external nonReentrant { 192 | OrderLib.Order memory o = order(id); 193 | require(block.timestamp < o.status, "status"); 194 | require(block.timestamp > o.filledTime + o.ask.fillDelay, "fill delay"); 195 | require( 196 | ERC20(o.ask.srcToken).allowance(o.maker, address(this)) < o.srcBidAmountNext() 197 | || ERC20(o.ask.srcToken).balanceOf(o.maker) < o.srcBidAmountNext(), 198 | "valid" 199 | ); 200 | status[id] = STATUS_CANCELED; 201 | o.status = STATUS_CANCELED; 202 | book[id] = o; 203 | emit OrderCanceled(o.id, o.maker, msg.sender); 204 | } 205 | 206 | /** 207 | * ---- internals ---- 208 | */ 209 | 210 | /** 211 | * verifies the bid against the ask params, reverts on invalid bid. 212 | * returns dstAmountOut after taker dstFee, which must be higher than any previous bid, unless previous bid is stale 213 | */ 214 | function verifyBid( 215 | OrderLib.Order memory o, 216 | address exchange, 217 | uint256 dstFee, 218 | uint32 slippagePercent, 219 | bytes calldata data 220 | ) private view returns (uint256 dstAmountOut) { 221 | require(block.timestamp < o.status, "status"); // deadline, canceled or completed 222 | require(block.timestamp > o.filledTime + o.ask.fillDelay, "fill delay"); 223 | require(o.ask.exchange == address(0) || o.ask.exchange == exchange, "exchange"); 224 | 225 | dstAmountOut = IExchange(exchange).getAmountOut( 226 | o.ask.srcToken, _dstToken(o), o.srcBidAmountNext(), o.ask.data, data, msg.sender 227 | ); 228 | dstAmountOut -= (dstAmountOut * slippagePercent) / PERCENT_BASE; 229 | dstAmountOut -= dstFee; 230 | 231 | require( 232 | dstAmountOut > (o.bid.dstAmount * MIN_OUTBID_PERCENT) / PERCENT_BASE // outbid by more than MIN_OUTBID_PERCENT 233 | || block.timestamp > o.bid.time + STALE_BID_SECONDS, // or stale bid 234 | "low bid" 235 | ); 236 | require(dstAmountOut >= o.dstMinAmountNext(), "min out"); 237 | verifyMakerBalance(o); 238 | } 239 | 240 | /** 241 | * executes the winning bid. reverts if bid no longer valid. 242 | * transfers next chunk srcToken amount from maker, swaps via bid exchange with bid data, transfers dstFee to taker (msg.sender) and 243 | * transfers all other dstToken amount to maker 244 | */ 245 | function performFill(OrderLib.Order memory o) 246 | private 247 | returns (address exchange, uint256 srcAmountIn, uint256 dstAmountOut, uint256 dstFee) 248 | { 249 | require(msg.sender == o.bid.taker, "taker"); 250 | require(block.timestamp < o.status, "status"); // deadline, canceled or completed 251 | require(block.timestamp > o.bid.time + o.ask.bidDelay, "bid delay"); 252 | 253 | exchange = o.bid.exchange; 254 | dstFee = o.bid.dstFee; 255 | srcAmountIn = o.srcBidAmountNext(); 256 | uint256 minOut = o.dstExpectedOutNext(); 257 | 258 | ERC20(o.ask.srcToken).safeTransferFrom(o.maker, address(this), srcAmountIn); 259 | srcAmountIn = ERC20(o.ask.srcToken).balanceOf(address(this)); // support FoT tokens 260 | ERC20(o.ask.srcToken).safeIncreaseAllowance(exchange, srcAmountIn); 261 | 262 | IExchange(exchange).swap( 263 | o.ask.srcToken, _dstToken(o), srcAmountIn, minOut + dstFee, o.ask.data, o.bid.data, msg.sender 264 | ); 265 | 266 | dstAmountOut = ERC20(_dstToken(o)).balanceOf(address(this)); // support FoT tokens 267 | dstAmountOut -= dstFee; 268 | require(dstAmountOut >= minOut, "min out"); 269 | 270 | if (o.ask.dstToken == address(0)) { 271 | IWETH(iweth).withdraw(ERC20(iweth).balanceOf(address(this))); 272 | Address.sendValue(payable(o.bid.taker), dstFee); 273 | Address.sendValue(payable(o.maker), dstAmountOut); 274 | } else { 275 | ERC20(_dstToken(o)).safeTransfer(o.bid.taker, dstFee); 276 | ERC20(_dstToken(o)).safeTransfer(o.maker, dstAmountOut); 277 | } 278 | } 279 | 280 | /** 281 | * reverts if maker does not hold enough balance srcToken or allowance to be spent here for the next chunk 282 | */ 283 | function verifyMakerBalance(OrderLib.Order memory o) private view { 284 | require(ERC20(o.ask.srcToken).allowance(o.maker, address(this)) >= o.srcBidAmountNext(), "maker allowance"); 285 | require(ERC20(o.ask.srcToken).balanceOf(o.maker) >= o.srcBidAmountNext(), "maker balance"); 286 | } 287 | 288 | function _dstToken(OrderLib.Order memory o) private view returns (address) { 289 | return o.ask.dstToken == address(0) ? iweth : o.ask.dstToken; 290 | } 291 | 292 | receive() external payable {} // solhint-disable-line no-empty-blocks 293 | } 294 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TWAP 2 | 3 | > Time-Weighted Average Price 4 | 5 | ## Security Audit Reports 6 | - ### [PeckShield](./Audit-Report-PeckShield.pdf) 7 | - ### [RDAuditors](./Audit-Report-RDAuditors.pdf) 8 | 9 | ## Summary 10 | 11 | This smart contract allows the incentivized execution of a [TWAP](https://en.wikipedia.org/wiki/Time-weighted_average_price) order (either a 12 | [Limit Order](https://en.wikipedia.org/wiki/Order_(exchange)#Limit_order) 13 | or a [Market Order](https://en.wikipedia.org/wiki/Order_(exchange)#Market_order)) 14 | **on any DEX**, with the possibility of partial fills. 15 | A TWAP order breaks a larger order down into smaller trades or "chunks", which are executed over a set period of time. 16 | This is a common strategy in traditional finance but it was not previously possible to execute such trades in a decentralized manner in DeFi systems. 17 | 18 | In this smart contract, users (makers) create orders that wait in the contract to be filled. Once made, these orders enable an 19 | [English Auction](https://en.wikipedia.org/wiki/English_auction) bidding war on each chunk at its time interval. 20 | Anyone willing to participate can serve as a “taker” by finding the best path to fill the order for the next chunk on any DEX, 21 | within the parameters set by the maker. Takers submit these paths as a bid to the contract, which selects the winner based on criteria described in detail below. 22 | 23 | The winning taker receives a portion of the output tokens as a reward for their effort. 24 | 25 | One honest taker (i.e., a taker who is willing to set the fee at the minimum amount needed to cover gas costs) 26 | is enough to ensure the entire system functions effectively at spot prices. 27 | 28 | The contract is set to operate only up to the year 2106 (32bit timestamps), at which point it will no longer be usable. 29 | 30 | > The `TWAP` Smart Contract does not hold any funds, has no owners, administrators, or other roles and is entirely immutable once deployed on an EVM blockchain. 31 | 32 | ## Use Cases 33 | 34 | TWAP has several major benefits for users and trading platforms alike. 35 | 36 | ### [Price impact](https://coinmarketcap.com/alexandria/glossary/price-impact) reduction 37 | TWAP orders can limit the disproportionate price impact that often occurs when making large orders or long-tail low liquidity token pairs, 38 | especially on DEX/AMMs where liquidity is often fragmented among pools and prices are volatile. 39 | 40 | Users should take into account the following considerations when using TWAP orders for this purpose: 41 | * The gap between chunks should be sufficient to give arbitrageurs the time to close any price discrepancies that might occur in the affected pools after a trade and bring the reserves back to equilibrium (on par with spot price). 42 | * Within this constraint, users should in most cases try to set the relevant time periods to be relatively short (i.e., a total duration of 5-30 minutes, with the interval 43 | between chunks of about 1-5 minutes). 44 | This allows the maker to enjoy maximum benefits, as keeping the order duration as short as possible minimizes the risk of market price volatility, while still allowing 45 | arbitrageurs sufficient time to close the gap, effectively increasing liquidity on the same pools. 46 | * Additional consideration should be given to the amount of chunks, as executing trades for more chunks requires the user to expend more gas costs on the total order, which can 47 | add up to more than the benefit gained from spreading over time. 48 | * Note that gas costs will eventually be rolled onto the `maker`, as takers will only be willing to bid if they can economically benefit from filling the transaction, 49 | which means that they will require a gas fee, at minimum. Users can therefore expect that, at minimum, a fee equal to the gas fee will be included by the taker in any bid. 50 | However, the presence of one honest taker who is willing to take only reimbursement for their gas fees as payment is enough for the entire system to function as efficiently as possible. 51 | * A limit order (tight `dstMinAmount`) may also be only partially filled if price moves away while the order is in flight. 52 | 53 | ### Long term [DCA strategy](https://en.wikipedia.org/wiki/Dollar_cost_averaging) 54 | Dollar-cost averaging (DCA) is an investing strategy where the investor purchases an asset or set of assets having a certain dollar value on a specific schedule 55 | (i.e., on the first day of every month). TWAP trades can be used to generate an automated version of this strategy. 56 | 57 | Users should take the following into account when using the TWAP contract for this purpose: 58 | * By setting duration to be very long in the future (even years in advance), setting a high allowance and holding the entire amount of tokens, a maker can effectively implement 59 | an automated DCA bot that is resilient to price manipulations and requires no other action from the maker. 60 | * Assuming there is one honest taker, setting a market order (near `0 dstMinAmount`) and a long fill delay will create a bidding war on the next chunk on a periodic basis (can be 61 | daily, weekly or monthly), while ensuring a price that is very close to spot market price. 62 | * The order will be visible on chain, and as bidding and execution can be predicted, some orders may be large enough to create an incentive for bidders to attempt to manipulate 63 | price across all markets just before it is executed. Due to this issue, utilizing TWAP for DCA is better reserved for large-cap "base" assets that are not easily susceptible to price manipulation of their entire market cap. 64 | 65 | ## Architecture 66 | 67 | ### Actors 68 | 69 | * `maker`: User, the Order creator. In order to execute orders, is required to have an input token (`srcToken`) balance and provide an approval to be swapped by the `TWAP` 70 | contract on a specific (or any) `exchange`. 71 | * Controls all Order restriction parameters such as limit price, expiration and the length of delay between chunks. 72 | * `taker`: Incentivized independant participators that monitor Orders submitted by makers. 73 | * Takers try to find the best path for relevant chunks and submit bids for those chunks, including a `fee` for the taker. 74 | * `dstFee`: **0 or more** of the `dstToken` output (i.e., the token the `maker` is purchasing) to be sent to the `taker` at the time the chunk is filled. 75 | * Spends the effort needed to find the best path, and risks being out-bid in the bidding war by another taker with a better path or lower fee. 76 | 77 | ### State diagram and execution flowchart 78 | 79 | ![TWAP diagram](./TWAP.png) 80 | 81 | #### [TWAP](./contracts/TWAP.sol) contract holds the order book, which is an array of all `Orders`. 82 | 83 | Before an Order can be filled, makers sign a transaction giving approval to the TWAP contract to remove the applicable input asset from their wallet. 84 | 85 | #### [Order](./contracts/OrderLib.sol) is created by a `maker`, who, after granting the approval, sends an `ask` transaction to the TWAP contract containing certain requested parameters (specified below). 86 | 87 | Takers monitor the Orders held by the contract, and can send bid transactions specifying the DEX on which it proposes to execute the next chunk, the output amount it can receive and the requested fee. 88 | As a result of this process, an `Order` is generated and held in the `TWAP` contract, which contains the following parameters and constraints: 89 | 90 | * `id`: the index in the order book, generated 91 | * `status`: canceled, completed, or deadline 92 | * `filledTime`: last chunk filled timestamp 93 | * `srcFilledAmount`: total filled amount in `srcToken` 94 | * `Ask`: holds the order parameters requested by the maker in the `ask` transaction 95 | * `time`: order creation timestamp 96 | * `deadline`: order duration timestamp, required 97 | * `bidDelay`: minimum delay in seconds before a bid can be filled, must be `>=MIN_BID_DELAY_SECONDS` 98 | * `fillDelay`: minimum delay in seconds between chunks 99 | * `maker`: order creator (`msg.sender`) 100 | * `exchange`: swap only on this exchange, or zero for any exchange 101 | * `srcToken`: input token, required 102 | * `dstToken`: output token, required. If zero address, will unwrap to native token on each chunk. 103 | * `srcAmount`: input total order amount in `srcToken`, required 104 | * `srcBidAmount`: input chunk size in `srcToken`, required 105 | * `dstMinAmount`: minimum output chunk size, in `dstToken`, required. Can be higher than market output (implies a limit order), or as low as 1 wei 106 | (implies a market order) 107 | * `Bid`: empty until the first `bid` received. Once bids begin to be received, holds the current winning bid information: 108 | * `time`: bid creation timestamp 109 | * `taker`: the winning bidder 110 | * `exchange`: execute bid on this exchange, never zero on a valid bid 111 | * `dstAmount`: output amount for this bid after fees in `dstToken` 112 | * `dstFee`: requested by `taker` for performing the bid and fill, in `dstToken`, may be `0` 113 | * `data`: swap data passed to exchange, expected output = `dstAmount` + `dstFee` 114 | 115 | #### Once an order is created, it waits in the contract to be filled, when viable 116 | 117 | #### The smart contract checks every bid transaction received to ensure that the following conditions are met: 118 | * The order has not been previously cancelled, and the deadline has not passed. 119 | * The maker granted the TWAP contract an `allowance` to swap the `srcToken`, with the approval covering an amount that is high enough to fill the next chunk. 120 | * The maker has a high enough `balance` of the `srcToken` to be swapped (for the next chunk). 121 | * The last chunk of the order was not filled too recently and there has been a sufficient delay between chunks. 122 | * The `fillDelay` is set by the order maker, minimum `MIN_FILL_DELAY_SECONDS` 123 | * If the order maker specified that a particular `exchange` would be utilized for the order, only that `exchange` can be used to swap. If the value is set to `zero`, any exchange can be used. 124 | * The current bid output after fees is higher than the previous winning bid. 125 | * The current bid output after fees is higher or equal to the minimum set by the order maker. 126 | * To avoid stale unfilled bids, a bid older than `MAX_BID_WINDOW_SECONDS` will be cleared and removed. 127 | 128 | Any invalid constraint will revert the `bid` transaction, so a successful transaction implies a win 129 | 130 | #### A winning bid can be filled (executed) only after a minimum delay of a specific time period set by the maker. 131 | This delay gives other bidders an opportunity to make a competing bid, allowing for a bidding war on the applicable chunk. 132 | 133 | * Each succesfsul bid allows for another `MIN_BID_WINDOW_SECONDS` interval to challenge it. 134 | * If no other bid is set as the new winner, the current `taker` (winning bidder) can fill the bid by calling `fill(id)`. 135 | * When receiving the `fill` transaction, the `TWAP` contract performs the same verifications as when bidding. 136 | * Once verified, the `fill` transaction also utilizes the allowance previously provided by the maker to perform the actual swap on the requested exchange, resulting in the 137 | transfer of the input tokens from the maker to the exchange, and the transfer of the output tokens from the exchange back to the maker. 138 | * If `dstFee` is set `>0`, the specified fee is paid out to the winning taker as part of the completion of the order, out of the `dstToken` amount of that swap. 139 | * All of the above transfers are contained and set within the valid `fill` transaction. 140 | 141 | #### This structure creates an incentive for takers to find a path that is advantageous for the bid, as a higher return of output tokens enables the fee to be as high as possible 142 | 143 | #### But, the presence of one honest taker, who is willing to set the fee such that it takes only enough to reimburse it for the gas fees incurred, will ensure that the order will be filled to as close a price to the spot market as possible. 144 | 145 | This is because this honest taker, by charging a lower fee, will propose a transaction containing the highest output to the maker and will therefore be selected as the winning 146 | bid when competing with those charging higher fees, all other things equal. 147 | Therefore, if the honest bidder is equally capable of locating the best path as any competing taker, the price received by the maker will be optimal. 148 | 149 | Please note the following additional features of the TWAP contract: 150 | 151 | * An order can be canceled any time by the maker. 152 | * An order can be only partially filled, due to market price volatility (unfilled limit orders), or expiration. 153 | * The maker must ensure that it has granted approval and has a sufficient balance of `srcToken` for each chunk before that chunk is available for bidding. It does not have to 154 | have the entire amount available and approved at the time of order creation. 155 | * Stale orders, as well as any orders that are invalid, can be `pruned` (canceled), by anyone. 156 | 157 | 158 | ## Build Setup 159 | - tests running on a specific block to ensure deterministic outcomes 160 | - create free accounts on Alchemy (or similar archive node provider), etherscan and coinmarketcap 161 | - create `.env` file with (or pass as environment variables): 162 | ``` 163 | NETWORK_URL_ETH="https://eth-mainnet.g.alchemy.com/v2/08***************************Kf" 164 | NETWORK_URL_POLY="https://polygon-mainnet.g.alchemy.com/v2/a7***************************xN" 165 | ETHERSCAN_ETH="VV***************************14" 166 | ETHERSCAN_POLY="9H***************************WY" 167 | COINMARKETCAP="81***************************36" 168 | ``` 169 | - workflow: 170 | - `forge test` 171 | 172 | [see tests output](./TEST_OUTPUT.md) 173 | -------------------------------------------------------------------------------- /lens.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "contract TWAP", 6 | "name": "_twap", 7 | "type": "address" 8 | } 9 | ], 10 | "stateMutability": "nonpayable", 11 | "type": "constructor" 12 | }, 13 | { 14 | "inputs": [ 15 | { 16 | "internalType": "address", 17 | "name": "token", 18 | "type": "address" 19 | }, 20 | { 21 | "internalType": "address", 22 | "name": "maker", 23 | "type": "address" 24 | }, 25 | { 26 | "internalType": "uint256", 27 | "name": "srcBidAmountNext", 28 | "type": "uint256" 29 | } 30 | ], 31 | "name": "hasAllowance", 32 | "outputs": [ 33 | { 34 | "internalType": "bool", 35 | "name": "", 36 | "type": "bool" 37 | } 38 | ], 39 | "stateMutability": "view", 40 | "type": "function" 41 | }, 42 | { 43 | "inputs": [ 44 | { 45 | "internalType": "address", 46 | "name": "token", 47 | "type": "address" 48 | }, 49 | { 50 | "internalType": "address", 51 | "name": "maker", 52 | "type": "address" 53 | }, 54 | { 55 | "internalType": "uint256", 56 | "name": "srcBidAmountNext", 57 | "type": "uint256" 58 | } 59 | ], 60 | "name": "hasBalance", 61 | "outputs": [ 62 | { 63 | "internalType": "bool", 64 | "name": "", 65 | "type": "bool" 66 | } 67 | ], 68 | "stateMutability": "view", 69 | "type": "function" 70 | }, 71 | { 72 | "inputs": [], 73 | "name": "length", 74 | "outputs": [ 75 | { 76 | "internalType": "uint64", 77 | "name": "", 78 | "type": "uint64" 79 | } 80 | ], 81 | "stateMutability": "view", 82 | "type": "function" 83 | }, 84 | { 85 | "inputs": [ 86 | { 87 | "internalType": "address", 88 | "name": "maker", 89 | "type": "address" 90 | } 91 | ], 92 | "name": "makerOrders", 93 | "outputs": [ 94 | { 95 | "components": [ 96 | { 97 | "internalType": "uint64", 98 | "name": "id", 99 | "type": "uint64" 100 | }, 101 | { 102 | "internalType": "uint32", 103 | "name": "status", 104 | "type": "uint32" 105 | }, 106 | { 107 | "internalType": "uint32", 108 | "name": "time", 109 | "type": "uint32" 110 | }, 111 | { 112 | "internalType": "uint32", 113 | "name": "filledTime", 114 | "type": "uint32" 115 | }, 116 | { 117 | "internalType": "uint256", 118 | "name": "srcFilledAmount", 119 | "type": "uint256" 120 | }, 121 | { 122 | "internalType": "address", 123 | "name": "maker", 124 | "type": "address" 125 | }, 126 | { 127 | "components": [ 128 | { 129 | "internalType": "address", 130 | "name": "exchange", 131 | "type": "address" 132 | }, 133 | { 134 | "internalType": "address", 135 | "name": "srcToken", 136 | "type": "address" 137 | }, 138 | { 139 | "internalType": "address", 140 | "name": "dstToken", 141 | "type": "address" 142 | }, 143 | { 144 | "internalType": "uint256", 145 | "name": "srcAmount", 146 | "type": "uint256" 147 | }, 148 | { 149 | "internalType": "uint256", 150 | "name": "srcBidAmount", 151 | "type": "uint256" 152 | }, 153 | { 154 | "internalType": "uint256", 155 | "name": "dstMinAmount", 156 | "type": "uint256" 157 | }, 158 | { 159 | "internalType": "uint32", 160 | "name": "deadline", 161 | "type": "uint32" 162 | }, 163 | { 164 | "internalType": "uint32", 165 | "name": "bidDelay", 166 | "type": "uint32" 167 | }, 168 | { 169 | "internalType": "uint32", 170 | "name": "fillDelay", 171 | "type": "uint32" 172 | }, 173 | { 174 | "internalType": "bytes", 175 | "name": "data", 176 | "type": "bytes" 177 | } 178 | ], 179 | "internalType": "struct OrderLib.Ask", 180 | "name": "ask", 181 | "type": "tuple" 182 | }, 183 | { 184 | "components": [ 185 | { 186 | "internalType": "uint32", 187 | "name": "time", 188 | "type": "uint32" 189 | }, 190 | { 191 | "internalType": "address", 192 | "name": "taker", 193 | "type": "address" 194 | }, 195 | { 196 | "internalType": "address", 197 | "name": "exchange", 198 | "type": "address" 199 | }, 200 | { 201 | "internalType": "uint256", 202 | "name": "dstAmount", 203 | "type": "uint256" 204 | }, 205 | { 206 | "internalType": "uint256", 207 | "name": "dstFee", 208 | "type": "uint256" 209 | }, 210 | { 211 | "internalType": "bytes", 212 | "name": "data", 213 | "type": "bytes" 214 | } 215 | ], 216 | "internalType": "struct OrderLib.Bid", 217 | "name": "bid", 218 | "type": "tuple" 219 | } 220 | ], 221 | "internalType": "struct OrderLib.Order[]", 222 | "name": "result", 223 | "type": "tuple[]" 224 | } 225 | ], 226 | "stateMutability": "view", 227 | "type": "function" 228 | }, 229 | { 230 | "inputs": [ 231 | { 232 | "internalType": "address", 233 | "name": "taker", 234 | "type": "address" 235 | }, 236 | { 237 | "internalType": "uint64", 238 | "name": "lastIndex", 239 | "type": "uint64" 240 | }, 241 | { 242 | "internalType": "uint64", 243 | "name": "pageSize", 244 | "type": "uint64" 245 | } 246 | ], 247 | "name": "takerBiddableOrders", 248 | "outputs": [ 249 | { 250 | "components": [ 251 | { 252 | "internalType": "uint64", 253 | "name": "id", 254 | "type": "uint64" 255 | }, 256 | { 257 | "internalType": "uint32", 258 | "name": "status", 259 | "type": "uint32" 260 | }, 261 | { 262 | "internalType": "uint32", 263 | "name": "time", 264 | "type": "uint32" 265 | }, 266 | { 267 | "internalType": "uint32", 268 | "name": "filledTime", 269 | "type": "uint32" 270 | }, 271 | { 272 | "internalType": "uint256", 273 | "name": "srcFilledAmount", 274 | "type": "uint256" 275 | }, 276 | { 277 | "internalType": "address", 278 | "name": "maker", 279 | "type": "address" 280 | }, 281 | { 282 | "components": [ 283 | { 284 | "internalType": "address", 285 | "name": "exchange", 286 | "type": "address" 287 | }, 288 | { 289 | "internalType": "address", 290 | "name": "srcToken", 291 | "type": "address" 292 | }, 293 | { 294 | "internalType": "address", 295 | "name": "dstToken", 296 | "type": "address" 297 | }, 298 | { 299 | "internalType": "uint256", 300 | "name": "srcAmount", 301 | "type": "uint256" 302 | }, 303 | { 304 | "internalType": "uint256", 305 | "name": "srcBidAmount", 306 | "type": "uint256" 307 | }, 308 | { 309 | "internalType": "uint256", 310 | "name": "dstMinAmount", 311 | "type": "uint256" 312 | }, 313 | { 314 | "internalType": "uint32", 315 | "name": "deadline", 316 | "type": "uint32" 317 | }, 318 | { 319 | "internalType": "uint32", 320 | "name": "bidDelay", 321 | "type": "uint32" 322 | }, 323 | { 324 | "internalType": "uint32", 325 | "name": "fillDelay", 326 | "type": "uint32" 327 | }, 328 | { 329 | "internalType": "bytes", 330 | "name": "data", 331 | "type": "bytes" 332 | } 333 | ], 334 | "internalType": "struct OrderLib.Ask", 335 | "name": "ask", 336 | "type": "tuple" 337 | }, 338 | { 339 | "components": [ 340 | { 341 | "internalType": "uint32", 342 | "name": "time", 343 | "type": "uint32" 344 | }, 345 | { 346 | "internalType": "address", 347 | "name": "taker", 348 | "type": "address" 349 | }, 350 | { 351 | "internalType": "address", 352 | "name": "exchange", 353 | "type": "address" 354 | }, 355 | { 356 | "internalType": "uint256", 357 | "name": "dstAmount", 358 | "type": "uint256" 359 | }, 360 | { 361 | "internalType": "uint256", 362 | "name": "dstFee", 363 | "type": "uint256" 364 | }, 365 | { 366 | "internalType": "bytes", 367 | "name": "data", 368 | "type": "bytes" 369 | } 370 | ], 371 | "internalType": "struct OrderLib.Bid", 372 | "name": "bid", 373 | "type": "tuple" 374 | } 375 | ], 376 | "internalType": "struct OrderLib.Order[]", 377 | "name": "result", 378 | "type": "tuple[]" 379 | } 380 | ], 381 | "stateMutability": "view", 382 | "type": "function" 383 | }, 384 | { 385 | "inputs": [ 386 | { 387 | "internalType": "address", 388 | "name": "taker", 389 | "type": "address" 390 | }, 391 | { 392 | "internalType": "uint64", 393 | "name": "lastIndex", 394 | "type": "uint64" 395 | }, 396 | { 397 | "internalType": "uint64", 398 | "name": "pageSize", 399 | "type": "uint64" 400 | } 401 | ], 402 | "name": "takerFillableOrders", 403 | "outputs": [ 404 | { 405 | "components": [ 406 | { 407 | "internalType": "uint64", 408 | "name": "id", 409 | "type": "uint64" 410 | }, 411 | { 412 | "internalType": "uint32", 413 | "name": "status", 414 | "type": "uint32" 415 | }, 416 | { 417 | "internalType": "uint32", 418 | "name": "time", 419 | "type": "uint32" 420 | }, 421 | { 422 | "internalType": "uint32", 423 | "name": "filledTime", 424 | "type": "uint32" 425 | }, 426 | { 427 | "internalType": "uint256", 428 | "name": "srcFilledAmount", 429 | "type": "uint256" 430 | }, 431 | { 432 | "internalType": "address", 433 | "name": "maker", 434 | "type": "address" 435 | }, 436 | { 437 | "components": [ 438 | { 439 | "internalType": "address", 440 | "name": "exchange", 441 | "type": "address" 442 | }, 443 | { 444 | "internalType": "address", 445 | "name": "srcToken", 446 | "type": "address" 447 | }, 448 | { 449 | "internalType": "address", 450 | "name": "dstToken", 451 | "type": "address" 452 | }, 453 | { 454 | "internalType": "uint256", 455 | "name": "srcAmount", 456 | "type": "uint256" 457 | }, 458 | { 459 | "internalType": "uint256", 460 | "name": "srcBidAmount", 461 | "type": "uint256" 462 | }, 463 | { 464 | "internalType": "uint256", 465 | "name": "dstMinAmount", 466 | "type": "uint256" 467 | }, 468 | { 469 | "internalType": "uint32", 470 | "name": "deadline", 471 | "type": "uint32" 472 | }, 473 | { 474 | "internalType": "uint32", 475 | "name": "bidDelay", 476 | "type": "uint32" 477 | }, 478 | { 479 | "internalType": "uint32", 480 | "name": "fillDelay", 481 | "type": "uint32" 482 | }, 483 | { 484 | "internalType": "bytes", 485 | "name": "data", 486 | "type": "bytes" 487 | } 488 | ], 489 | "internalType": "struct OrderLib.Ask", 490 | "name": "ask", 491 | "type": "tuple" 492 | }, 493 | { 494 | "components": [ 495 | { 496 | "internalType": "uint32", 497 | "name": "time", 498 | "type": "uint32" 499 | }, 500 | { 501 | "internalType": "address", 502 | "name": "taker", 503 | "type": "address" 504 | }, 505 | { 506 | "internalType": "address", 507 | "name": "exchange", 508 | "type": "address" 509 | }, 510 | { 511 | "internalType": "uint256", 512 | "name": "dstAmount", 513 | "type": "uint256" 514 | }, 515 | { 516 | "internalType": "uint256", 517 | "name": "dstFee", 518 | "type": "uint256" 519 | }, 520 | { 521 | "internalType": "bytes", 522 | "name": "data", 523 | "type": "bytes" 524 | } 525 | ], 526 | "internalType": "struct OrderLib.Bid", 527 | "name": "bid", 528 | "type": "tuple" 529 | } 530 | ], 531 | "internalType": "struct OrderLib.Order[]", 532 | "name": "result", 533 | "type": "tuple[]" 534 | } 535 | ], 536 | "stateMutability": "view", 537 | "type": "function" 538 | }, 539 | { 540 | "inputs": [], 541 | "name": "twap", 542 | "outputs": [ 543 | { 544 | "internalType": "contract TWAP", 545 | "name": "", 546 | "type": "address" 547 | } 548 | ], 549 | "stateMutability": "view", 550 | "type": "function" 551 | } 552 | ] 553 | -------------------------------------------------------------------------------- /configs-old.json: -------------------------------------------------------------------------------- 1 | { 2 | "SpookySwap": { 3 | "chainName": "ftm", 4 | "chainId": 250, 5 | "twapVersion": 4, 6 | "twapAddress": "0xdb55107c17Cb433D322052BFA36069fDf7Cb1031", 7 | "lensAddress": "0x0628DC34b758AC652bf64fFe924e6B62F136dE3c", 8 | "takers": [ 9 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 10 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 11 | ], 12 | "bidDelaySeconds": 60, 13 | "minChunkSizeUsd": 10, 14 | "name": "SpookySwap", 15 | "partner": "Orbs:TWAP:SpookySwap", 16 | "exchangeAddress": "0x3924d62219483015f982b160d48c0fa5Fd436Cba", 17 | "exchangeType": "RouterExchange", 18 | "pathfinderKey": "" 19 | }, 20 | "SpookySwapSonic": { 21 | "chainName": "sonic", 22 | "chainId": 146, 23 | "twapVersion": 4, 24 | "twapAddress": "0xe94E30ebe3d438dD1e7FE686503E392b1A601566", 25 | "lensAddress": "0x4bd562529fbda83196d8D1d79cc8404c75Ff5A3A", 26 | "takers": [ 27 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 28 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 29 | ], 30 | "bidDelaySeconds": 60, 31 | "minChunkSizeUsd": 10, 32 | "name": "SpookySwapSonic", 33 | "partner": "Orbs:TWAP:SpookySwap", 34 | "exchangeAddress": "0xAd97B770ad64aE47fc7d64B3bD820dCDbF9ff7DA", 35 | "exchangeType": "RouterExchange", 36 | "pathfinderKey": "" 37 | }, 38 | "Pangolin": { 39 | "chainName": "avax", 40 | "chainId": 43114, 41 | "twapVersion": 4, 42 | "twapAddress": "0xF2687e119B0A4aB00bED9c9F425403566D605020", 43 | "lensAddress": "0x2C5E9Aa3631106DaE1B7420347577974b162548f", 44 | "takers": [ 45 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 46 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 47 | ], 48 | "bidDelaySeconds": 60, 49 | "minChunkSizeUsd": 100, 50 | "name": "Pangolin", 51 | "partner": "Orbs:TWAP:Pangolin", 52 | "exchangeAddress": "0xf2d96E7BE676153d202e1453804E2749923C7c5b", 53 | "exchangeType": "UniswapV2Exchange", 54 | "pathfinderKey": "PangolinSwap" 55 | }, 56 | "PangolinDaas": { 57 | "chainName": "avax", 58 | "chainId": 43114, 59 | "twapVersion": 4, 60 | "twapAddress": "0xF2687e119B0A4aB00bED9c9F425403566D605020", 61 | "lensAddress": "0x2C5E9Aa3631106DaE1B7420347577974b162548f", 62 | "takers": [ 63 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 64 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 65 | ], 66 | "bidDelaySeconds": 60, 67 | "minChunkSizeUsd": 100, 68 | "name": "PangolinDaas", 69 | "partner": "Orbs:TWAP:PangolinDaas", 70 | "exchangeAddress": "0x1579EED0527781B1A748043AA1f59a3858Ace4a7", 71 | "exchangeType": "PangolinDaasExchange", 72 | "pathfinderKey": "PangolinSwap" 73 | }, 74 | "QuickSwap": { 75 | "chainName": "poly", 76 | "chainId": 137, 77 | "twapVersion": 4, 78 | "twapAddress": "0xceFf098C9199c5d9cf24078dc14Eb8F787631cC0", 79 | "lensAddress": "0xd68676ABd169bDcAE3CAF2D474A5C3759B140C0B", 80 | "takers": [ 81 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 82 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 83 | ], 84 | "bidDelaySeconds": 60, 85 | "minChunkSizeUsd": 10, 86 | "name": "QuickSwap", 87 | "partner": "Orbs:TWAP:QuickSwap", 88 | "exchangeAddress": "0x26D0ec4Be402BCE03AAa8aAf0CF67e9428ba54eF", 89 | "exchangeType": "ParaswapExchange", 90 | "pathfinderKey": "QuickSwap,QuickSwapV3" 91 | }, 92 | "Chronos": { 93 | "chainName": "arb", 94 | "chainId": 42161, 95 | "twapVersion": 4, 96 | "twapAddress": "0xD63430c74C8E70D9dbdCA04C6a9E6E9E929028DA", 97 | "lensAddress": "0x7eD30e55FF792816Ba88f42b43ee0c4512302152", 98 | "takers": [ 99 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 100 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 101 | ], 102 | "bidDelaySeconds": 60, 103 | "minChunkSizeUsd": 50, 104 | "name": "Chronos", 105 | "partner": "Orbs:TWAP:Chronos", 106 | "exchangeAddress": "0xceFf098C9199c5d9cf24078dc14Eb8F787631cC0", 107 | "exchangeType": "OdosExchange", 108 | "pathfinderKey": "Chronos Stable,Chronos Volatile,Chronos V3" 109 | }, 110 | "BaseSwap": { 111 | "chainName": "base", 112 | "chainId": 8453, 113 | "twapVersion": 4, 114 | "twapAddress": "0x25a0A78f5ad07b2474D3D42F1c1432178465936d", 115 | "lensAddress": "0xFaB090A976c61FAfc2bAf91f04222fea42A42257", 116 | "takers": [ 117 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 118 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 119 | ], 120 | "bidDelaySeconds": 60, 121 | "minChunkSizeUsd": 50, 122 | "name": "BaseSwap", 123 | "partner": "Orbs:TWAP:BaseSwap", 124 | "exchangeAddress": "0xeFE1B6096838949156e5130604434A2a13c68C68", 125 | "exchangeType": "OdosExchange", 126 | "pathfinderKey": "BaseSwap,BaseSwapX" 127 | }, 128 | "Arbidex": { 129 | "chainName": "arb", 130 | "chainId": 42161, 131 | "twapVersion": 4, 132 | "twapAddress": "0xD63430c74C8E70D9dbdCA04C6a9E6E9E929028DA", 133 | "lensAddress": "0x7eD30e55FF792816Ba88f42b43ee0c4512302152", 134 | "takers": [ 135 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 136 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 137 | ], 138 | "bidDelaySeconds": 60, 139 | "minChunkSizeUsd": 50, 140 | "name": "Arbidex", 141 | "partner": "Orbs:TWAP:Arbidex", 142 | "exchangeAddress": "0x8ffde23Fba2d7Aea9C3CBf2d5B7B533BB46754a8", 143 | "exchangeType": "OdosExchange", 144 | "pathfinderKey": "Arbidex Classic,Arbidex Quantum" 145 | }, 146 | "Thena": { 147 | "chainName": "bsc", 148 | "chainId": 56, 149 | "twapVersion": 4, 150 | "twapAddress": "0x25a0A78f5ad07b2474D3D42F1c1432178465936d", 151 | "lensAddress": "0xFaB090A976c61FAfc2bAf91f04222fea42A42257", 152 | "takers": [ 153 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 154 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 155 | ], 156 | "bidDelaySeconds": 60, 157 | "minChunkSizeUsd": 50, 158 | "name": "Thena", 159 | "partner": "Orbs:TWAP:Thena", 160 | "exchangeAddress": "0xc2aBC02acd77Bb2407efA22348dA9afC8B375290", 161 | "exchangeType": "OpenOceanExchange", 162 | "pathfinderKey": "43,47" 163 | }, 164 | "PancakeSwap": { 165 | "chainName": "bsc", 166 | "chainId": 56, 167 | "twapVersion": 4, 168 | "twapAddress": "0x25a0A78f5ad07b2474D3D42F1c1432178465936d", 169 | "lensAddress": "0xFaB090A976c61FAfc2bAf91f04222fea42A42257", 170 | "takers": [ 171 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 172 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 173 | ], 174 | "bidDelaySeconds": 60, 175 | "minChunkSizeUsd": 50, 176 | "name": "PancakeSwap", 177 | "partner": "Orbs:TWAP:PancakeSwap", 178 | "exchangeAddress": "0xb2BAFe188faD927240038cC4FfF2d771d8A58905", 179 | "exchangeType": "RouterExchange", 180 | "pathfinderKey": "" 181 | }, 182 | "PancakeSwapArbitrum": { 183 | "chainName": "arb", 184 | "chainId": 42161, 185 | "twapVersion": 4, 186 | "twapAddress": "0xD63430c74C8E70D9dbdCA04C6a9E6E9E929028DA", 187 | "lensAddress": "0x7eD30e55FF792816Ba88f42b43ee0c4512302152", 188 | "takers": [ 189 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 190 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 191 | ], 192 | "bidDelaySeconds": 60, 193 | "minChunkSizeUsd": 50, 194 | "name": "PancakeSwap", 195 | "partner": "Orbs:TWAP:PancakeSwap", 196 | "exchangeAddress": "0xE20167871dB616DdfFD0Fd870d9bC068C350DD1F", 197 | "exchangeType": "RouterExchange", 198 | "pathfinderKey": "" 199 | }, 200 | "PancakeSwapBase": { 201 | "chainName": "base", 202 | "chainId": 8453, 203 | "twapVersion": 4, 204 | "twapAddress": "0x25a0A78f5ad07b2474D3D42F1c1432178465936d", 205 | "lensAddress": "0xFaB090A976c61FAfc2bAf91f04222fea42A42257", 206 | "takers": [ 207 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 208 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 209 | ], 210 | "bidDelaySeconds": 60, 211 | "minChunkSizeUsd": 50, 212 | "name": "PancakeSwap", 213 | "partner": "Orbs:TWAP:PancakeSwap", 214 | "exchangeAddress": "0x10ed1F36e4eBE76E161c9AADDa20BE841bc0082c", 215 | "exchangeType": "RouterExchange", 216 | "pathfinderKey": "" 217 | }, 218 | "PancakeSwapLinea": { 219 | "chainName": "linea", 220 | "chainId": 59144, 221 | "twapVersion": 4, 222 | "twapAddress": "0xbE7984eb48E2102549338B533dcF68035e413F66", 223 | "lensAddress": "0x7a2885a470a96995Fa99177e068fd554e3aCAbB3", 224 | "takers": [ 225 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 226 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 227 | ], 228 | "bidDelaySeconds": 60, 229 | "minChunkSizeUsd": 100, 230 | "name": "PancakeSwap", 231 | "partner": "Orbs:TWAP:PancakeSwap", 232 | "exchangeAddress": "0x10ed1F36e4eBE76E161c9AADDa20BE841bc0082c", 233 | "exchangeType": "RouterExchange", 234 | "pathfinderKey": "" 235 | }, 236 | "Lynex": { 237 | "chainName": "linea", 238 | "chainId": 59144, 239 | "twapVersion": 4, 240 | "twapAddress": "0xbE7984eb48E2102549338B533dcF68035e413F66", 241 | "lensAddress": "0x7a2885a470a96995Fa99177e068fd554e3aCAbB3", 242 | "takers": [ 243 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 244 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 245 | ], 246 | "bidDelaySeconds": 60, 247 | "minChunkSizeUsd": 100, 248 | "name": "Lynex", 249 | "partner": "Orbs:TWAP:Lynex", 250 | "exchangeAddress": "0x72e3e1fD5D2Ee2F1C2Eb695206D490a1D45C3835", 251 | "exchangeType": "OpenOceanExchange", 252 | "pathfinderKey": "19,18" 253 | }, 254 | "SyncSwap": { 255 | "chainName": "zkSync", 256 | "chainId": 324, 257 | "twapVersion": 4, 258 | "twapAddress": "0x971f855C98f45fcdD2782f03bD80Cf6C146Cf123", 259 | "lensAddress": "0x1157A4A2517Aa3471B08FE076eE3a2DF49EA449D", 260 | "takers": [ 261 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 262 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 263 | ], 264 | "bidDelaySeconds": 60, 265 | "minChunkSizeUsd": 100, 266 | "name": "SyncSwap", 267 | "partner": "Orbs:TWAP:SyncSwap", 268 | "exchangeAddress": "0x5D96A072B2854d9a9D56C68806b0Bbcf7Db60b6d", 269 | "exchangeType": "OpenOceanExchange", 270 | "pathfinderKey": "2,3" 271 | }, 272 | "Retro": { 273 | "chainName": "poly", 274 | "chainId": 137, 275 | "twapVersion": 4, 276 | "twapAddress": "0xceFf098C9199c5d9cf24078dc14Eb8F787631cC0", 277 | "lensAddress": "0xd68676ABd169bDcAE3CAF2D474A5C3759B140C0B", 278 | "takers": [ 279 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 280 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 281 | ], 282 | "bidDelaySeconds": 60, 283 | "minChunkSizeUsd": 10, 284 | "name": "Retro", 285 | "partner": "Orbs:TWAP:Retro", 286 | "exchangeAddress": "0xC454Abb5b0CA974a4397139764478C736327d2B0", 287 | "exchangeType": "KyberExchange", 288 | "pathfinderKey": "retro,retro-v3" 289 | }, 290 | "SushiArb": { 291 | "chainName": "arb", 292 | "chainId": 42161, 293 | "twapVersion": 4, 294 | "twapAddress": "0xD63430c74C8E70D9dbdCA04C6a9E6E9E929028DA", 295 | "lensAddress": "0x7eD30e55FF792816Ba88f42b43ee0c4512302152", 296 | "takers": [ 297 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 298 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 299 | ], 300 | "bidDelaySeconds": 60, 301 | "minChunkSizeUsd": 50, 302 | "name": "SushiArb", 303 | "partner": "Orbs:TWAP:Sushi", 304 | "exchangeAddress": "0x08c41f5D1C844061f6D952E25827eeAA576c6536", 305 | "exchangeType": "RouterExchange", 306 | "pathfinderKey": "" 307 | }, 308 | "SushiBase": { 309 | "chainName": "base", 310 | "chainId": 8453, 311 | "twapVersion": 4, 312 | "twapAddress": "0x25a0A78f5ad07b2474D3D42F1c1432178465936d", 313 | "lensAddress": "0xFaB090A976c61FAfc2bAf91f04222fea42A42257", 314 | "takers": [ 315 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 316 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 317 | ], 318 | "bidDelaySeconds": 60, 319 | "minChunkSizeUsd": 50, 320 | "name": "SushiBase", 321 | "partner": "Orbs:TWAP:Sushi", 322 | "exchangeAddress": "0x08c41f5D1C844061f6D952E25827eeAA576c6536", 323 | "exchangeType": "RouterExchange", 324 | "pathfinderKey": "" 325 | }, 326 | "SushiEth": { 327 | "chainName": "eth", 328 | "chainId": 1, 329 | "twapVersion": 4, 330 | "twapAddress": "0x037E2bda7B1f03411ba5E96ACb7F36a7D19c3D83", 331 | "lensAddress": "0xD5Dc89eb7fc256b407A0Cd166e9d20bcF8FbCf98", 332 | "takers": [ 333 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 334 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 335 | ], 336 | "bidDelaySeconds": 60, 337 | "minChunkSizeUsd": 200, 338 | "name": "SushiEth", 339 | "partner": "Orbs:TWAP:Sushi", 340 | "exchangeAddress": "0x08c41f5D1C844061f6D952E25827eeAA576c6536", 341 | "exchangeType": "RouterExchange", 342 | "pathfinderKey": "" 343 | }, 344 | "DragonSwap": { 345 | "chainName": "sei", 346 | "chainId": 1329, 347 | "twapVersion": 4, 348 | "twapAddress": "0x18C3cEFb4b90bc854b1F7b1852b9862374a43778", 349 | "lensAddress": "0xebDB65Bd47b303D82cb64012aE7b402CA877E2A3", 350 | "takers": [ 351 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 352 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 353 | ], 354 | "bidDelaySeconds": 60, 355 | "minChunkSizeUsd": 50, 356 | "name": "DragonSwap", 357 | "partner": "Orbs:TWAP:DragonSwap", 358 | "exchangeAddress": "0x101e1B65Bb516FB5f4547C80BAe0b51f1b8D7a22", 359 | "exchangeType": "RouterExchange", 360 | "pathfinderKey": "" 361 | }, 362 | "TeaFi": { 363 | "chainName": "poly", 364 | "chainId": 137, 365 | "twapVersion": 4, 366 | "twapAddress": "0xceFf098C9199c5d9cf24078dc14Eb8F787631cC0", 367 | "lensAddress": "0xd68676ABd169bDcAE3CAF2D474A5C3759B140C0B", 368 | "takers": [ 369 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 370 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 371 | ], 372 | "bidDelaySeconds": 60, 373 | "minChunkSizeUsd": 10, 374 | "name": "TeaFi", 375 | "partner": "Orbs:TWAP:TeaFi", 376 | "exchangeAddress": "0x4e2000DC371704c9CbcAeb8e7baff6813fb83063", 377 | "exchangeType": "ParaswapExchange", 378 | "pathfinderKey": "AaveV2,AaveV3,AaveV3Stata,AngleStakedStableEUR,AngleStakedStableUSD,ApeSwap,AugustusRFQ,BalancerV2,ComethSwap,CurveV1,CurveV1Factory,CurveV1StableNg,CurveV2,Dfyn,DODOV2,Dystopia,Hashflow,IronV2,JarvisV6,KyberDmm,QuickSwap,QuickSwapV3,Retro,SushiSwap,SushiSwapV3,SwaapV2,Synapse,UniswapV2,UniswapV3,WaultFinance,Wmatic,WooFiV2,wUSDM" 379 | }, 380 | "H2Finance": { 381 | "chainName": "cronos-zkevm", 382 | "chainId": 388, 383 | "twapVersion": 4, 384 | "twapAddress": "0xc3945b7A39De6518Ed852f6355706F46F672c096", 385 | "lensAddress": "0xc30Fb187D68Bc032B68769425B8f9Ad7EFC663Fc", 386 | "takers": [], 387 | "bidDelaySeconds": 60, 388 | "minChunkSizeUsd": 10, 389 | "name": "H2Finance", 390 | "partner": "Orbs:TWAP:H2Finance", 391 | "exchangeAddress": "", 392 | "exchangeType": "" 393 | }, 394 | "Ocelex": { 395 | "chainName": "zircuit", 396 | "chainId": 48900, 397 | "twapVersion": 4, 398 | "twapAddress": "0x647413e85A5D38B08A19c3765F12C6A5A64E6051", 399 | "lensAddress": "0xAbaea785f5a9C61190209A0Ae32F6C7F73A43d4E", 400 | "bidDelaySeconds": 60, 401 | "minChunkSizeUsd": 10, 402 | "name": "Ocelex", 403 | "partner": "Orbs:TWAP:Ocelex", 404 | "exchangeAddress": "", 405 | "exchangeType": "" 406 | }, 407 | "SparkDEX": { 408 | "chainName": "flare", 409 | "chainId": 14, 410 | "twapVersion": 4, 411 | "twapAddress": "0xac9d2216369A6339465Ee2D6a32306dd4884E299", 412 | "lensAddress": "0xAFfd7D0128aaFd325AC4685fB7b807885e180758", 413 | "takers": [ 414 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 415 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 416 | ], 417 | "bidDelaySeconds": 60, 418 | "minChunkSizeUsd": 10, 419 | "name": "SparkDEX", 420 | "partner": "Orbs:TWAP:SparkDEX", 421 | "exchangeAddress": "0xf5CCFE12878623f15F5B377052a06F20975A0B60", 422 | "exchangeType": "RouterExchange" 423 | }, 424 | "Ramses": { 425 | "chainName": "arb", 426 | "chainId": 42161, 427 | "twapVersion": 4, 428 | "twapAddress": "0xD63430c74C8E70D9dbdCA04C6a9E6E9E929028DA", 429 | "lensAddress": "0x7eD30e55FF792816Ba88f42b43ee0c4512302152", 430 | "takers": [ 431 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 432 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 433 | ], 434 | "bidDelaySeconds": 60, 435 | "minChunkSizeUsd": 50, 436 | "name": "Ramses", 437 | "partner": "Orbs:TWAP:Ramses", 438 | "exchangeAddress": "0x02dfBec67664E343F5F1F41Ba59F3E4Ba273A2F7", 439 | "exchangeType": "RouterExchange", 440 | "pathfinderKey": "" 441 | }, 442 | "Fenix": { 443 | "chainName": "blast", 444 | "chainId": 81457, 445 | "twapVersion": 4, 446 | "twapAddress": "0x3ad4f4c2e4d9B928aE6ACaeF465152DC3208Db50", 447 | "lensAddress": "0x54D41aAC6e02325b7c83825843936F1A8b8bB47A", 448 | "takers": [ 449 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 450 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 451 | ], 452 | "bidDelaySeconds": 60, 453 | "minChunkSizeUsd": 10, 454 | "name": "Fenix", 455 | "partner": "Orbs:TWAP:Fenix", 456 | "exchangeAddress": "", 457 | "exchangeType": "", 458 | "pathfinderKey": "" 459 | }, 460 | "SwapX": { 461 | "chainName": "sonic", 462 | "chainId": 146, 463 | "twapVersion": 4, 464 | "twapAddress": "0xe94E30ebe3d438dD1e7FE686503E392b1A601566", 465 | "lensAddress": "0x4bd562529fbda83196d8D1d79cc8404c75Ff5A3A", 466 | "takers": [ 467 | "0xbAC000A86039e669BF9D659ffBd474734c59a8b1", 468 | "0x333000AC360964E949Fac4660E9508c9D50E14c9" 469 | ], 470 | "bidDelaySeconds": 60, 471 | "minChunkSizeUsd": 10, 472 | "name": "SwapX", 473 | "partner": "Orbs:TWAP:SwapX", 474 | "exchangeAddress": "0xE5012eBDe5e26EE3Ea41992154731a03023CF274", 475 | "exchangeType": "RouterExchange", 476 | "pathfinderKey": "" 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /configs.json: -------------------------------------------------------------------------------- 1 | { 2 | "SpookySwap": { 3 | "chainName": "ftm", 4 | "chainId": 250, 5 | "twapVersion": 4, 6 | "twapAddress": "0xd3B290FEB04E353d1821bc0a12397FdEa9a846C0", 7 | "lensAddress": "0x0221EfDF1Fd3212AF87F23cceB7693a65fAF1d7f", 8 | "takers": [ 9 | "0xDF406A27C58a8Cd6Fd43e143339bCE131216a913", 10 | "0x504f6E10173249dD22491829D98862Cf81DeF79E" 11 | ], 12 | "bidDelaySeconds": 60, 13 | "minChunkSizeUsd": 10, 14 | "name": "SpookySwap", 15 | "partner": "Orbs:TWAP:SpookySwap", 16 | "exchangeAddress": "0xdF7CCd5fc7077E9de5f27a7b7bfDC837c82f8496", 17 | "exchangeType": "ExchangeV2", 18 | "pathfinderKey": "" 19 | }, 20 | "SpookySwapSonic": { 21 | "chainName": "sonic", 22 | "chainId": 146, 23 | "twapVersion": 4, 24 | "twapAddress": "0x8963992816b4EafE5a22b7DB2A99513c18be9afA", 25 | "lensAddress": "0x67e631F71232D63AcA98a2D5E9B2Bce5FCc39d5D", 26 | "takers": [ 27 | "0xDF406A27C58a8Cd6Fd43e143339bCE131216a913", 28 | "0x504f6E10173249dD22491829D98862Cf81DeF79E" 29 | ], 30 | "bidDelaySeconds": 60, 31 | "minChunkSizeUsd": 10, 32 | "name": "SpookySwapSonic", 33 | "partner": "Orbs:TWAP:SpookySwap", 34 | "exchangeAddress": "0x6699bE3aF75e5c1B807b1031dBde6dA9A67739F3", 35 | "exchangeType": "ExchangeV2", 36 | "pathfinderKey": "" 37 | }, 38 | "QuickSwap": { 39 | "chainName": "poly", 40 | "chainId": 137, 41 | "twapVersion": 4, 42 | "twapAddress": "0x688C027B0f7FaCeFcBa73e472900d28c12C5bDF4", 43 | "lensAddress": "0xe0D4E736fc76af7C256ae7652c8c1e850bfb7849", 44 | "takers": [ 45 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 46 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 47 | ], 48 | "bidDelaySeconds": 60, 49 | "minChunkSizeUsd": 10, 50 | "name": "QuickSwap", 51 | "partner": "Orbs:TWAP:QuickSwap", 52 | "exchangeAddress": "0x8FCc245209bE85C49D738D0CE5613F74E5d91E86", 53 | "exchangeType": "ParaswapExchange", 54 | "pathfinderKey": "QuickSwap,QuickSwapV3" 55 | }, 56 | "QuickSwapBase": { 57 | "chainName": "base", 58 | "chainId": 8453, 59 | "twapVersion": 4, 60 | "twapAddress": "0xc918bdC47264687796Cd54FE362FaC4f8b99Eb55", 61 | "lensAddress": "0x6313188c1909b161074D62E43105faC9B756A23e", 62 | "takers": [ 63 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 64 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 65 | ], 66 | "bidDelaySeconds": 60, 67 | "minChunkSizeUsd": 10, 68 | "name": "QuickSwap", 69 | "partner": "Orbs:TWAP:QuickSwap", 70 | "exchangeAddress": "0xb7a3d74895bfd3aff6780525e36d79fcf26a895f", 71 | "exchangeType": "ExchangeV2", 72 | "pathfinderKey": "" 73 | }, 74 | "Chronos": { 75 | "chainName": "arb", 76 | "chainId": 42161, 77 | "twapVersion": 4, 78 | "twapAddress": "0x0B94dcC0EA2d1ee33Ab064DaC252de980a941eF3", 79 | "lensAddress": "0x549e1fc9a47FCc0C5C2EbdfF31254cc49fF7164e", 80 | "takers": [ 81 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 82 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 83 | ], 84 | "bidDelaySeconds": 60, 85 | "minChunkSizeUsd": 50, 86 | "name": "Chronos", 87 | "partner": "Orbs:TWAP:Chronos", 88 | "exchangeAddress": "0x0178e50adAF3b925F27aC518FCd952CE7F9A3B36", 89 | "exchangeType": "ExchangeV2", 90 | "pathfinderKey": "Chronos Stable,Chronos Volatile,Chronos V3" 91 | }, 92 | "BaseSwap": { 93 | "chainName": "base", 94 | "chainId": 8453, 95 | "twapVersion": 4, 96 | "twapAddress": "0xc918bdC47264687796Cd54FE362FaC4f8b99Eb55", 97 | "lensAddress": "0x6313188c1909b161074D62E43105faC9B756A23e", 98 | "takers": [ 99 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 100 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 101 | ], 102 | "bidDelaySeconds": 60, 103 | "minChunkSizeUsd": 50, 104 | "name": "BaseSwap", 105 | "partner": "Orbs:TWAP:BaseSwap", 106 | "exchangeAddress": "0xAca3155c17530C4aA96746F226fcC049658490d2", 107 | "exchangeType": "ExchangeV2", 108 | "pathfinderKey": "BaseSwap,BaseSwapX" 109 | }, 110 | "Arbidex": { 111 | "chainName": "arb", 112 | "chainId": 42161, 113 | "twapVersion": 4, 114 | "twapAddress": "0x0B94dcC0EA2d1ee33Ab064DaC252de980a941eF3", 115 | "lensAddress": "0x549e1fc9a47FCc0C5C2EbdfF31254cc49fF7164e", 116 | "takers": [ 117 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 118 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 119 | ], 120 | "bidDelaySeconds": 60, 121 | "minChunkSizeUsd": 50, 122 | "name": "Arbidex", 123 | "partner": "Orbs:TWAP:Arbidex", 124 | "exchangeAddress": "0x49d21052f4695d16C7cce6eF561301Aa4d840420", 125 | "exchangeType": "ExchangeV2", 126 | "pathfinderKey": "Arbidex Classic,Arbidex Quantum" 127 | }, 128 | "Thena": { 129 | "chainName": "bsc", 130 | "chainId": 56, 131 | "twapVersion": 4, 132 | "twapAddress": "0xa6F7444D2b92Aa9F94a2165c77aAF2B671e63994", 133 | "lensAddress": "0xEdB0c077fa87Fb21d050c619FF426798f8Fc1264", 134 | "takers": [ 135 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 136 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 137 | ], 138 | "bidDelaySeconds": 60, 139 | "minChunkSizeUsd": 50, 140 | "name": "Thena", 141 | "partner": "Orbs:TWAP:Thena", 142 | "exchangeAddress": "0x2B2fABDbfa4a15da0d351F947C14F4520db0bDc1", 143 | "exchangeType": "ExchangeV2", 144 | "pathfinderKey": "43,47" 145 | }, 146 | "PancakeSwap": { 147 | "chainName": "bsc", 148 | "chainId": 56, 149 | "twapVersion": 4, 150 | "twapAddress": "0xa6F7444D2b92Aa9F94a2165c77aAF2B671e63994", 151 | "lensAddress": "0xEdB0c077fa87Fb21d050c619FF426798f8Fc1264", 152 | "takers": [ 153 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 154 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 155 | ], 156 | "bidDelaySeconds": 60, 157 | "minChunkSizeUsd": 50, 158 | "name": "PancakeSwap", 159 | "partner": "Orbs:TWAP:PancakeSwap", 160 | "exchangeAddress": "0x1A2bb6B75D58b740d88413ef4840D6fa3F637940", 161 | "exchangeType": "P2Exchange", 162 | "pathfinderKey": "" 163 | }, 164 | "PancakeSwapArbitrum": { 165 | "chainName": "arb", 166 | "chainId": 42161, 167 | "twapVersion": 4, 168 | "twapAddress": "0x0B94dcC0EA2d1ee33Ab064DaC252de980a941eF3", 169 | "lensAddress": "0x549e1fc9a47FCc0C5C2EbdfF31254cc49fF7164e", 170 | "takers": [ 171 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 172 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 173 | ], 174 | "bidDelaySeconds": 60, 175 | "minChunkSizeUsd": 50, 176 | "name": "PancakeSwap", 177 | "partner": "Orbs:TWAP:PancakeSwap", 178 | "exchangeAddress": "0xb37cB9A058c03081Ae6EF934313588cD53d408e7", 179 | "exchangeType": "P2Exchange", 180 | "pathfinderKey": "" 181 | }, 182 | "PancakeSwapBase": { 183 | "chainName": "base", 184 | "chainId": 8453, 185 | "twapVersion": 4, 186 | "twapAddress": "0xc918bdC47264687796Cd54FE362FaC4f8b99Eb55", 187 | "lensAddress": "0x6313188c1909b161074D62E43105faC9B756A23e", 188 | "takers": [ 189 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 190 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 191 | ], 192 | "bidDelaySeconds": 60, 193 | "minChunkSizeUsd": 50, 194 | "name": "PancakeSwap", 195 | "partner": "Orbs:TWAP:PancakeSwap", 196 | "exchangeAddress": "0xb37cB9A058c03081Ae6EF934313588cD53d408e7", 197 | "exchangeType": "P2Exchange", 198 | "pathfinderKey": "" 199 | }, 200 | "PancakeSwapLinea": { 201 | "chainName": "linea", 202 | "chainId": 59144, 203 | "twapVersion": 4, 204 | "twapAddress": "0x48423e62acbfEF7779b5b4a5E7d6Fbd39E623d78", 205 | "lensAddress": "0xe84CaEc86eCF3f0AB4267dC6130D9a5510e73DFb", 206 | "takers": [ 207 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 208 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 209 | ], 210 | "bidDelaySeconds": 60, 211 | "minChunkSizeUsd": 100, 212 | "name": "PancakeSwap", 213 | "partner": "Orbs:TWAP:PancakeSwap", 214 | "exchangeAddress": "0xb37cB9A058c03081Ae6EF934313588cD53d408e7", 215 | "exchangeType": "P2Exchange", 216 | "pathfinderKey": "" 217 | }, 218 | "Lynex": { 219 | "chainName": "linea", 220 | "chainId": 59144, 221 | "twapVersion": 4, 222 | "twapAddress": "0x48423e62acbfEF7779b5b4a5E7d6Fbd39E623d78", 223 | "lensAddress": "0xe84CaEc86eCF3f0AB4267dC6130D9a5510e73DFb", 224 | "takers": [ 225 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 226 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 227 | ], 228 | "bidDelaySeconds": 60, 229 | "minChunkSizeUsd": 100, 230 | "name": "Lynex", 231 | "partner": "Orbs:TWAP:Lynex", 232 | "exchangeAddress": "0x04C06C96d7D19977156016DD408B5992af0570a2", 233 | "exchangeType": "ExchangeV2", 234 | "pathfinderKey": "19,18" 235 | }, 236 | "SyncSwap": { 237 | "chainName": "zkSync", 238 | "chainId": 324, 239 | "twapVersion": 4, 240 | "twapAddress": "0xc32cAA0F7AD9ED88e22Fc214fc24cCb5C0595D68", 241 | "lensAddress": "0x7daE5af3011c3bE9A5283F0368a3fdD757fc74F9", 242 | "takers": [ 243 | "0xB924C20c400575D2B73Baa16CF0Fa0EAA720aFBC", 244 | "0xBE452F03Be354CAC397C3F0c8397672067D44531" 245 | ], 246 | "bidDelaySeconds": 60, 247 | "minChunkSizeUsd": 100, 248 | "name": "SyncSwap", 249 | "partner": "Orbs:TWAP:SyncSwap", 250 | "exchangeAddress": "0x422623a40c42f026Ed33A9910078Aa843FD66e27", 251 | "exchangeType": "ExchangeV2", 252 | "pathfinderKey": "2,3" 253 | }, 254 | "Retro": { 255 | "chainName": "poly", 256 | "chainId": 137, 257 | "twapVersion": 4, 258 | "twapAddress": "0x688C027B0f7FaCeFcBa73e472900d28c12C5bDF4", 259 | "lensAddress": "0xe0D4E736fc76af7C256ae7652c8c1e850bfb7849", 260 | "takers": [ 261 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 262 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 263 | ], 264 | "bidDelaySeconds": 60, 265 | "minChunkSizeUsd": 10, 266 | "name": "Retro", 267 | "partner": "Orbs:TWAP:Retro", 268 | "exchangeAddress": "0xb7A3d74895bFD3Aff6780525e36D79FCf26a895F", 269 | "exchangeType": "ExchangeV2", 270 | "pathfinderKey": "retro,retro-v3" 271 | }, 272 | "SushiArb": { 273 | "chainName": "arb", 274 | "chainId": 42161, 275 | "twapVersion": 4, 276 | "twapAddress": "0x0B94dcC0EA2d1ee33Ab064DaC252de980a941eF3", 277 | "lensAddress": "0x549e1fc9a47FCc0C5C2EbdfF31254cc49fF7164e", 278 | "takers": [ 279 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 280 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 281 | ], 282 | "bidDelaySeconds": 60, 283 | "minChunkSizeUsd": 50, 284 | "name": "SushiArb", 285 | "partner": "Orbs:TWAP:Sushi", 286 | "exchangeAddress": "0x04eB53119079FA779492720D1EfeAEBF0aF2e5ad", 287 | "exchangeType": "ExchangeV2", 288 | "pathfinderKey": "" 289 | }, 290 | "SushiBase": { 291 | "chainName": "base", 292 | "chainId": 8453, 293 | "twapVersion": 4, 294 | "twapAddress": "0xc918bdC47264687796Cd54FE362FaC4f8b99Eb55", 295 | "lensAddress": "0x6313188c1909b161074D62E43105faC9B756A23e", 296 | "takers": [ 297 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 298 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 299 | ], 300 | "bidDelaySeconds": 60, 301 | "minChunkSizeUsd": 50, 302 | "name": "SushiBase", 303 | "partner": "Orbs:TWAP:Sushi", 304 | "exchangeAddress": "0x04eB53119079FA779492720D1EfeAEBF0aF2e5ad", 305 | "exchangeType": "ExchangeV2", 306 | "pathfinderKey": "" 307 | }, 308 | "SushiEth": { 309 | "chainName": "eth", 310 | "chainId": 1, 311 | "twapVersion": 4, 312 | "twapAddress": "0xb1ed8BCAD1EaC8a1DF0764700472391800D12946", 313 | "lensAddress": "0x0967f448c4d4dbd14c355E635AE9CbF68cc44A60", 314 | "takers": [ 315 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 316 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 317 | ], 318 | "bidDelaySeconds": 60, 319 | "minChunkSizeUsd": 200, 320 | "name": "SushiEth", 321 | "partner": "Orbs:TWAP:Sushi", 322 | "exchangeAddress": "0x04eB53119079FA779492720D1EfeAEBF0aF2e5ad", 323 | "exchangeType": "ExchangeV2", 324 | "pathfinderKey": "" 325 | }, 326 | "SushiKatana": { 327 | "chainName": "katana", 328 | "chainId": 747474, 329 | "twapVersion": 4, 330 | "twapAddress": "0xf2d96E7BE676153d202e1453804E2749923C7c5b", 331 | "lensAddress": "0x1579EED0527781B1A748043AA1f59a3858Ace4a7", 332 | "takers": [ 333 | "0xF74437A3Fc45a518640828E5D6A3E8c9A9BbDC3a", 334 | "0x07A6C715063e94c0D95e809efefFCbF4EBEdfD55" 335 | ], 336 | "bidDelaySeconds": 60, 337 | "minChunkSizeUsd": 50, 338 | "name": "SushiKatana", 339 | "partner": "Orbs:TWAP:Sushi", 340 | "exchangeAddress": "0x92209481507e6B2d14C9b5b70Ed287024177220E", 341 | "exchangeType": "ExchangeV2", 342 | "pathfinderKey": "" 343 | }, 344 | "DragonSwap": { 345 | "chainName": "sei", 346 | "chainId": 1329, 347 | "twapVersion": 4, 348 | "twapAddress": "0xde737dB24548F8d41A4a3Ca2Bac8aaaDc4DBA099", 349 | "lensAddress": "0xa1376f2Bb80D3cF6c2D8ebEf34b3d122e9af4020", 350 | "takers": [ 351 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 352 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 353 | ], 354 | "bidDelaySeconds": 60, 355 | "minChunkSizeUsd": 50, 356 | "name": "DragonSwap", 357 | "partner": "Orbs:TWAP:DragonSwap", 358 | "exchangeAddress": "0xf2F933FafbDB97062CfA3c447ff373e76A90Efd6", 359 | "exchangeType": "ExchangeV2", 360 | "pathfinderKey": "" 361 | }, 362 | "TeaFi": { 363 | "chainName": "poly", 364 | "chainId": 137, 365 | "twapVersion": 4, 366 | "twapAddress": "0x688C027B0f7FaCeFcBa73e472900d28c12C5bDF4", 367 | "lensAddress": "0xe0D4E736fc76af7C256ae7652c8c1e850bfb7849", 368 | "takers": [ 369 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 370 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 371 | ], 372 | "bidDelaySeconds": 60, 373 | "minChunkSizeUsd": 10, 374 | "name": "TeaFi", 375 | "partner": "Orbs:TWAP:TeaFi", 376 | "exchangeAddress": "0xE9D3b4Cf5B0CC392e78C1a4DD9885D449EEF9793", 377 | "exchangeType": "ParaswapExchange", 378 | "pathfinderKey": "AaveV2,AaveV3,AaveV3Stata,AngleStakedStableEUR,AngleStakedStableUSD,ApeSwap,AugustusRFQ,BalancerV2,ComethSwap,CurveV1,CurveV1Factory,CurveV1StableNg,CurveV2,Dfyn,DODOV2,Dystopia,Hashflow,IronV2,JarvisV6,KyberDmm,QuickSwap,QuickSwapV3,Retro,SushiSwap,SushiSwapV3,SwaapV2,Synapse,UniswapV2,UniswapV3,WaultFinance,Wmatic,WooFiV2,wUSDM" 379 | }, 380 | "SparkDEX": { 381 | "chainName": "flare", 382 | "chainId": 14, 383 | "twapVersion": 4, 384 | "twapAddress": "0x9D70B0b90915Bb8b9bdAC7e6a7e6435bBF1feC4D", 385 | "lensAddress": "0xf2d96E7BE676153d202e1453804E2749923C7c5b", 386 | "takers": [ 387 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 388 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 389 | ], 390 | "bidDelaySeconds": 60, 391 | "minChunkSizeUsd": 10, 392 | "name": "SparkDEX", 393 | "partner": "Orbs:TWAP:SparkDEX", 394 | "exchangeAddress": "0xe59c53C76bB7EEc01401A18fA8215B94bC65Bf56", 395 | "exchangeType": "P2Exchange" 396 | }, 397 | "Ramses": { 398 | "chainName": "arb", 399 | "chainId": 42161, 400 | "twapVersion": 4, 401 | "twapAddress": "0x0B94dcC0EA2d1ee33Ab064DaC252de980a941eF3", 402 | "lensAddress": "0x549e1fc9a47FCc0C5C2EbdfF31254cc49fF7164e", 403 | "takers": [ 404 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 405 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 406 | ], 407 | "bidDelaySeconds": 60, 408 | "minChunkSizeUsd": 50, 409 | "name": "Ramses", 410 | "partner": "Orbs:TWAP:Ramses", 411 | "exchangeAddress": "0x8FCc245209bE85C49D738D0CE5613F74E5d91E86", 412 | "exchangeType": "ParaswapExchange", 413 | "pathfinderKey": "" 414 | }, 415 | "SwapX": { 416 | "chainName": "sonic", 417 | "chainId": 146, 418 | "twapVersion": 4, 419 | "twapAddress": "0x8963992816b4EafE5a22b7DB2A99513c18be9afA", 420 | "lensAddress": "0x67e631F71232D63AcA98a2D5E9B2Bce5FCc39d5D", 421 | "takers": [ 422 | "0xDF406A27C58a8Cd6Fd43e143339bCE131216a913", 423 | "0x504f6E10173249dD22491829D98862Cf81DeF79E" 424 | ], 425 | "bidDelaySeconds": 60, 426 | "minChunkSizeUsd": 10, 427 | "name": "SwapX", 428 | "partner": "Orbs:TWAP:SwapX", 429 | "exchangeAddress": "0xDA902994b7F7a1ecDd8De02E4a17dbFF2E6F67b7", 430 | "exchangeType": "ExchangeV2", 431 | "pathfinderKey": "" 432 | }, 433 | "H2Finance": { 434 | "chainName": "cronos-zkevm", 435 | "chainId": 388, 436 | "twapVersion": 4, 437 | "twapAddress": "0xc32cAA0F7AD9ED88e22Fc214fc24cCb5C0595D68", 438 | "lensAddress": "0x7daE5af3011c3bE9A5283F0368a3fdD757fc74F9", 439 | "takers": [ 440 | "0xB924C20c400575D2B73Baa16CF0Fa0EAA720aFBC", 441 | "0xBE452F03Be354CAC397C3F0c8397672067D44531" 442 | ], 443 | "bidDelaySeconds": 60, 444 | "minChunkSizeUsd": 10, 445 | "name": "H2Finance", 446 | "partner": "Orbs:TWAP:H2Finance", 447 | "exchangeAddress": "0x2f1f10dc311722Be609e6B427bF3FBac6e44093B", 448 | "exchangeType": "ExchangeV2" 449 | }, 450 | "Shadow": { 451 | "chainName": "sonic", 452 | "chainId": 146, 453 | "twapVersion": 4, 454 | "twapAddress": "0x8963992816b4EafE5a22b7DB2A99513c18be9afA", 455 | "lensAddress": "0x67e631F71232D63AcA98a2D5E9B2Bce5FCc39d5D", 456 | "takers": [ 457 | "0xDF406A27C58a8Cd6Fd43e143339bCE131216a913", 458 | "0x504f6E10173249dD22491829D98862Cf81DeF79E" 459 | ], 460 | "bidDelaySeconds": 60, 461 | "minChunkSizeUsd": 10, 462 | "name": "Shadow", 463 | "partner": "Orbs:TWAP:Shadow", 464 | "exchangeAddress": "0x69e3c891450161b7BF27f181eE85E7bAE69E5b07", 465 | "exchangeType": "ExchangeV2" 466 | }, 467 | "Kodiak": { 468 | "chainName": "berachain", 469 | "chainId": 80094, 470 | "twapVersion": 4, 471 | "twapAddress": "0xD13609A8ace04D11Ea2FFE176B69dF77C6d9375E", 472 | "lensAddress": "0xA0b07F9a11dFb01388149abBdbc5B4f2196600AB", 473 | "takers": [ 474 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 475 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 476 | ], 477 | "bidDelaySeconds": 60, 478 | "minChunkSizeUsd": 10, 479 | "name": "Kodiak", 480 | "partner": "Orbs:TWAP:Kodiak", 481 | "exchangeAddress": "0x69B7aE1250B8d1309954CaD65b6607ae478Ec6eE", 482 | "exchangeType": "ExchangeV2" 483 | }, 484 | "OmniBase": { 485 | "chainName": "base", 486 | "chainId": 8453, 487 | "twapVersion": 4, 488 | "twapAddress": "0xc918bdC47264687796Cd54FE362FaC4f8b99Eb55", 489 | "lensAddress": "0x6313188c1909b161074D62E43105faC9B756A23e", 490 | "takers": [ 491 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 492 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 493 | ], 494 | "bidDelaySeconds": 60, 495 | "minChunkSizeUsd": 10, 496 | "name": "OmniBase", 497 | "partner": "Orbs:TWAP:OmniBase", 498 | "exchangeAddress": "0x54435FcFDF3A08150Ed799b6EF472A20a432303F", 499 | "exchangeType": "ParaswapExchange" 500 | }, 501 | "OmniArb": { 502 | "chainName": "arb", 503 | "chainId": 42161, 504 | "twapVersion": 4, 505 | "twapAddress": "0x0B94dcC0EA2d1ee33Ab064DaC252de980a941eF3", 506 | "lensAddress": "0x549e1fc9a47FCc0C5C2EbdfF31254cc49fF7164e", 507 | "takers": [ 508 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 509 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 510 | ], 511 | "bidDelaySeconds": 60, 512 | "minChunkSizeUsd": 50, 513 | "name": "OmniArb", 514 | "partner": "Orbs:TWAP:OmniArb", 515 | "exchangeAddress": "0xF0607785424d342Cceb33E708f4579CA97a3b392", 516 | "exchangeType": "ParaswapExchange" 517 | }, 518 | "BlackholeAvax": { 519 | "chainName": "avax", 520 | "chainId": 43114, 521 | "twapVersion": 4, 522 | "twapAddress": "0xf77Ad005aBF7e31f669ce89a6568B2f39Ca92cDe", 523 | "lensAddress": "0x48466B82Ce1575c5c9eCa168ac1090048dD47fA3", 524 | "takers": [ 525 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 526 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 527 | ], 528 | "bidDelaySeconds": 60, 529 | "minChunkSizeUsd": 50, 530 | "name": "BlackholeAvax", 531 | "partner": "Orbs:TWAP:BlackholeAvax", 532 | "exchangeAddress": "0xb7A3d74895bFD3Aff6780525e36D79FCf26a895F", 533 | "exchangeType": "ExchangeV2" 534 | }, 535 | "NewEraBnb": { 536 | "chainName": "bsc", 537 | "chainId": 56, 538 | "twapVersion": 4, 539 | "twapAddress": "0xa6F7444D2b92Aa9F94a2165c77aAF2B671e63994", 540 | "lensAddress": "0xEdB0c077fa87Fb21d050c619FF426798f8Fc1264", 541 | "takers": [ 542 | "0xA05405b6340A7F43dC5835351BFC4f5b1F028359", 543 | "0xE3Efef1563a5960ACc731F9e4d6f4cBf5bd87dcA" 544 | ], 545 | "bidDelaySeconds": 60, 546 | "minChunkSizeUsd": 50, 547 | "name": "NewEraBnb", 548 | "partner": "Orbs:TWAP:NewEraBnb", 549 | "exchangeAddress": "0xA22D7Df24d9Ea4D4EF6dC96b2204bd550430eC42", 550 | "exchangeType": "ExchangeV2" 551 | } 552 | } 553 | -------------------------------------------------------------------------------- /twap.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_iweth", 7 | "type": "address" 8 | } 9 | ], 10 | "stateMutability": "nonpayable", 11 | "type": "constructor" 12 | }, 13 | { 14 | "anonymous": false, 15 | "inputs": [ 16 | { 17 | "indexed": true, 18 | "internalType": "uint64", 19 | "name": "id", 20 | "type": "uint64" 21 | }, 22 | { 23 | "indexed": true, 24 | "internalType": "address", 25 | "name": "maker", 26 | "type": "address" 27 | }, 28 | { 29 | "indexed": true, 30 | "internalType": "address", 31 | "name": "exchange", 32 | "type": "address" 33 | }, 34 | { 35 | "indexed": false, 36 | "internalType": "uint32", 37 | "name": "slippagePercent", 38 | "type": "uint32" 39 | }, 40 | { 41 | "components": [ 42 | { 43 | "internalType": "uint32", 44 | "name": "time", 45 | "type": "uint32" 46 | }, 47 | { 48 | "internalType": "address", 49 | "name": "taker", 50 | "type": "address" 51 | }, 52 | { 53 | "internalType": "address", 54 | "name": "exchange", 55 | "type": "address" 56 | }, 57 | { 58 | "internalType": "uint256", 59 | "name": "dstAmount", 60 | "type": "uint256" 61 | }, 62 | { 63 | "internalType": "uint256", 64 | "name": "dstFee", 65 | "type": "uint256" 66 | }, 67 | { 68 | "internalType": "bytes", 69 | "name": "data", 70 | "type": "bytes" 71 | } 72 | ], 73 | "indexed": false, 74 | "internalType": "struct OrderLib.Bid", 75 | "name": "bid", 76 | "type": "tuple" 77 | } 78 | ], 79 | "name": "OrderBid", 80 | "type": "event" 81 | }, 82 | { 83 | "anonymous": false, 84 | "inputs": [ 85 | { 86 | "indexed": true, 87 | "internalType": "uint64", 88 | "name": "id", 89 | "type": "uint64" 90 | }, 91 | { 92 | "indexed": true, 93 | "internalType": "address", 94 | "name": "maker", 95 | "type": "address" 96 | }, 97 | { 98 | "indexed": false, 99 | "internalType": "address", 100 | "name": "sender", 101 | "type": "address" 102 | } 103 | ], 104 | "name": "OrderCanceled", 105 | "type": "event" 106 | }, 107 | { 108 | "anonymous": false, 109 | "inputs": [ 110 | { 111 | "indexed": true, 112 | "internalType": "uint64", 113 | "name": "id", 114 | "type": "uint64" 115 | }, 116 | { 117 | "indexed": true, 118 | "internalType": "address", 119 | "name": "maker", 120 | "type": "address" 121 | }, 122 | { 123 | "indexed": true, 124 | "internalType": "address", 125 | "name": "exchange", 126 | "type": "address" 127 | }, 128 | { 129 | "indexed": false, 130 | "internalType": "address", 131 | "name": "taker", 132 | "type": "address" 133 | } 134 | ], 135 | "name": "OrderCompleted", 136 | "type": "event" 137 | }, 138 | { 139 | "anonymous": false, 140 | "inputs": [ 141 | { 142 | "indexed": true, 143 | "internalType": "uint64", 144 | "name": "id", 145 | "type": "uint64" 146 | }, 147 | { 148 | "indexed": true, 149 | "internalType": "address", 150 | "name": "maker", 151 | "type": "address" 152 | }, 153 | { 154 | "indexed": true, 155 | "internalType": "address", 156 | "name": "exchange", 157 | "type": "address" 158 | }, 159 | { 160 | "components": [ 161 | { 162 | "internalType": "address", 163 | "name": "exchange", 164 | "type": "address" 165 | }, 166 | { 167 | "internalType": "address", 168 | "name": "srcToken", 169 | "type": "address" 170 | }, 171 | { 172 | "internalType": "address", 173 | "name": "dstToken", 174 | "type": "address" 175 | }, 176 | { 177 | "internalType": "uint256", 178 | "name": "srcAmount", 179 | "type": "uint256" 180 | }, 181 | { 182 | "internalType": "uint256", 183 | "name": "srcBidAmount", 184 | "type": "uint256" 185 | }, 186 | { 187 | "internalType": "uint256", 188 | "name": "dstMinAmount", 189 | "type": "uint256" 190 | }, 191 | { 192 | "internalType": "uint32", 193 | "name": "deadline", 194 | "type": "uint32" 195 | }, 196 | { 197 | "internalType": "uint32", 198 | "name": "bidDelay", 199 | "type": "uint32" 200 | }, 201 | { 202 | "internalType": "uint32", 203 | "name": "fillDelay", 204 | "type": "uint32" 205 | }, 206 | { 207 | "internalType": "bytes", 208 | "name": "data", 209 | "type": "bytes" 210 | } 211 | ], 212 | "indexed": false, 213 | "internalType": "struct OrderLib.Ask", 214 | "name": "ask", 215 | "type": "tuple" 216 | } 217 | ], 218 | "name": "OrderCreated", 219 | "type": "event" 220 | }, 221 | { 222 | "anonymous": false, 223 | "inputs": [ 224 | { 225 | "indexed": true, 226 | "internalType": "uint64", 227 | "name": "id", 228 | "type": "uint64" 229 | }, 230 | { 231 | "indexed": true, 232 | "internalType": "address", 233 | "name": "maker", 234 | "type": "address" 235 | }, 236 | { 237 | "indexed": true, 238 | "internalType": "address", 239 | "name": "exchange", 240 | "type": "address" 241 | }, 242 | { 243 | "indexed": false, 244 | "internalType": "address", 245 | "name": "taker", 246 | "type": "address" 247 | }, 248 | { 249 | "indexed": false, 250 | "internalType": "uint256", 251 | "name": "srcAmountIn", 252 | "type": "uint256" 253 | }, 254 | { 255 | "indexed": false, 256 | "internalType": "uint256", 257 | "name": "dstAmountOut", 258 | "type": "uint256" 259 | }, 260 | { 261 | "indexed": false, 262 | "internalType": "uint256", 263 | "name": "dstFee", 264 | "type": "uint256" 265 | }, 266 | { 267 | "indexed": false, 268 | "internalType": "uint256", 269 | "name": "srcFilledAmount", 270 | "type": "uint256" 271 | } 272 | ], 273 | "name": "OrderFilled", 274 | "type": "event" 275 | }, 276 | { 277 | "inputs": [], 278 | "name": "MIN_BID_DELAY_SECONDS", 279 | "outputs": [ 280 | { 281 | "internalType": "uint32", 282 | "name": "", 283 | "type": "uint32" 284 | } 285 | ], 286 | "stateMutability": "view", 287 | "type": "function" 288 | }, 289 | { 290 | "inputs": [], 291 | "name": "MIN_OUTBID_PERCENT", 292 | "outputs": [ 293 | { 294 | "internalType": "uint32", 295 | "name": "", 296 | "type": "uint32" 297 | } 298 | ], 299 | "stateMutability": "view", 300 | "type": "function" 301 | }, 302 | { 303 | "inputs": [], 304 | "name": "PERCENT_BASE", 305 | "outputs": [ 306 | { 307 | "internalType": "uint32", 308 | "name": "", 309 | "type": "uint32" 310 | } 311 | ], 312 | "stateMutability": "view", 313 | "type": "function" 314 | }, 315 | { 316 | "inputs": [], 317 | "name": "STALE_BID_SECONDS", 318 | "outputs": [ 319 | { 320 | "internalType": "uint32", 321 | "name": "", 322 | "type": "uint32" 323 | } 324 | ], 325 | "stateMutability": "view", 326 | "type": "function" 327 | }, 328 | { 329 | "inputs": [], 330 | "name": "STATUS_CANCELED", 331 | "outputs": [ 332 | { 333 | "internalType": "uint32", 334 | "name": "", 335 | "type": "uint32" 336 | } 337 | ], 338 | "stateMutability": "view", 339 | "type": "function" 340 | }, 341 | { 342 | "inputs": [], 343 | "name": "STATUS_COMPLETED", 344 | "outputs": [ 345 | { 346 | "internalType": "uint32", 347 | "name": "", 348 | "type": "uint32" 349 | } 350 | ], 351 | "stateMutability": "view", 352 | "type": "function" 353 | }, 354 | { 355 | "inputs": [], 356 | "name": "VERSION", 357 | "outputs": [ 358 | { 359 | "internalType": "uint8", 360 | "name": "", 361 | "type": "uint8" 362 | } 363 | ], 364 | "stateMutability": "view", 365 | "type": "function" 366 | }, 367 | { 368 | "inputs": [ 369 | { 370 | "components": [ 371 | { 372 | "internalType": "address", 373 | "name": "exchange", 374 | "type": "address" 375 | }, 376 | { 377 | "internalType": "address", 378 | "name": "srcToken", 379 | "type": "address" 380 | }, 381 | { 382 | "internalType": "address", 383 | "name": "dstToken", 384 | "type": "address" 385 | }, 386 | { 387 | "internalType": "uint256", 388 | "name": "srcAmount", 389 | "type": "uint256" 390 | }, 391 | { 392 | "internalType": "uint256", 393 | "name": "srcBidAmount", 394 | "type": "uint256" 395 | }, 396 | { 397 | "internalType": "uint256", 398 | "name": "dstMinAmount", 399 | "type": "uint256" 400 | }, 401 | { 402 | "internalType": "uint32", 403 | "name": "deadline", 404 | "type": "uint32" 405 | }, 406 | { 407 | "internalType": "uint32", 408 | "name": "bidDelay", 409 | "type": "uint32" 410 | }, 411 | { 412 | "internalType": "uint32", 413 | "name": "fillDelay", 414 | "type": "uint32" 415 | }, 416 | { 417 | "internalType": "bytes", 418 | "name": "data", 419 | "type": "bytes" 420 | } 421 | ], 422 | "internalType": "struct OrderLib.Ask", 423 | "name": "_ask", 424 | "type": "tuple" 425 | } 426 | ], 427 | "name": "ask", 428 | "outputs": [ 429 | { 430 | "internalType": "uint64", 431 | "name": "id", 432 | "type": "uint64" 433 | } 434 | ], 435 | "stateMutability": "nonpayable", 436 | "type": "function" 437 | }, 438 | { 439 | "inputs": [ 440 | { 441 | "internalType": "uint64", 442 | "name": "id", 443 | "type": "uint64" 444 | }, 445 | { 446 | "internalType": "address", 447 | "name": "exchange", 448 | "type": "address" 449 | }, 450 | { 451 | "internalType": "uint256", 452 | "name": "dstFee", 453 | "type": "uint256" 454 | }, 455 | { 456 | "internalType": "uint32", 457 | "name": "slippagePercent", 458 | "type": "uint32" 459 | }, 460 | { 461 | "internalType": "bytes", 462 | "name": "data", 463 | "type": "bytes" 464 | } 465 | ], 466 | "name": "bid", 467 | "outputs": [], 468 | "stateMutability": "nonpayable", 469 | "type": "function" 470 | }, 471 | { 472 | "inputs": [ 473 | { 474 | "internalType": "uint256", 475 | "name": "", 476 | "type": "uint256" 477 | } 478 | ], 479 | "name": "book", 480 | "outputs": [ 481 | { 482 | "internalType": "uint64", 483 | "name": "id", 484 | "type": "uint64" 485 | }, 486 | { 487 | "internalType": "uint32", 488 | "name": "status", 489 | "type": "uint32" 490 | }, 491 | { 492 | "internalType": "uint32", 493 | "name": "time", 494 | "type": "uint32" 495 | }, 496 | { 497 | "internalType": "uint32", 498 | "name": "filledTime", 499 | "type": "uint32" 500 | }, 501 | { 502 | "internalType": "uint256", 503 | "name": "srcFilledAmount", 504 | "type": "uint256" 505 | }, 506 | { 507 | "internalType": "address", 508 | "name": "maker", 509 | "type": "address" 510 | }, 511 | { 512 | "components": [ 513 | { 514 | "internalType": "address", 515 | "name": "exchange", 516 | "type": "address" 517 | }, 518 | { 519 | "internalType": "address", 520 | "name": "srcToken", 521 | "type": "address" 522 | }, 523 | { 524 | "internalType": "address", 525 | "name": "dstToken", 526 | "type": "address" 527 | }, 528 | { 529 | "internalType": "uint256", 530 | "name": "srcAmount", 531 | "type": "uint256" 532 | }, 533 | { 534 | "internalType": "uint256", 535 | "name": "srcBidAmount", 536 | "type": "uint256" 537 | }, 538 | { 539 | "internalType": "uint256", 540 | "name": "dstMinAmount", 541 | "type": "uint256" 542 | }, 543 | { 544 | "internalType": "uint32", 545 | "name": "deadline", 546 | "type": "uint32" 547 | }, 548 | { 549 | "internalType": "uint32", 550 | "name": "bidDelay", 551 | "type": "uint32" 552 | }, 553 | { 554 | "internalType": "uint32", 555 | "name": "fillDelay", 556 | "type": "uint32" 557 | }, 558 | { 559 | "internalType": "bytes", 560 | "name": "data", 561 | "type": "bytes" 562 | } 563 | ], 564 | "internalType": "struct OrderLib.Ask", 565 | "name": "ask", 566 | "type": "tuple" 567 | }, 568 | { 569 | "components": [ 570 | { 571 | "internalType": "uint32", 572 | "name": "time", 573 | "type": "uint32" 574 | }, 575 | { 576 | "internalType": "address", 577 | "name": "taker", 578 | "type": "address" 579 | }, 580 | { 581 | "internalType": "address", 582 | "name": "exchange", 583 | "type": "address" 584 | }, 585 | { 586 | "internalType": "uint256", 587 | "name": "dstAmount", 588 | "type": "uint256" 589 | }, 590 | { 591 | "internalType": "uint256", 592 | "name": "dstFee", 593 | "type": "uint256" 594 | }, 595 | { 596 | "internalType": "bytes", 597 | "name": "data", 598 | "type": "bytes" 599 | } 600 | ], 601 | "internalType": "struct OrderLib.Bid", 602 | "name": "bid", 603 | "type": "tuple" 604 | } 605 | ], 606 | "stateMutability": "view", 607 | "type": "function" 608 | }, 609 | { 610 | "inputs": [ 611 | { 612 | "internalType": "uint64", 613 | "name": "id", 614 | "type": "uint64" 615 | } 616 | ], 617 | "name": "cancel", 618 | "outputs": [], 619 | "stateMutability": "nonpayable", 620 | "type": "function" 621 | }, 622 | { 623 | "inputs": [ 624 | { 625 | "internalType": "uint64", 626 | "name": "id", 627 | "type": "uint64" 628 | } 629 | ], 630 | "name": "fill", 631 | "outputs": [], 632 | "stateMutability": "nonpayable", 633 | "type": "function" 634 | }, 635 | { 636 | "inputs": [], 637 | "name": "iweth", 638 | "outputs": [ 639 | { 640 | "internalType": "address", 641 | "name": "", 642 | "type": "address" 643 | } 644 | ], 645 | "stateMutability": "view", 646 | "type": "function" 647 | }, 648 | { 649 | "inputs": [], 650 | "name": "length", 651 | "outputs": [ 652 | { 653 | "internalType": "uint64", 654 | "name": "", 655 | "type": "uint64" 656 | } 657 | ], 658 | "stateMutability": "view", 659 | "type": "function" 660 | }, 661 | { 662 | "inputs": [ 663 | { 664 | "internalType": "address", 665 | "name": "", 666 | "type": "address" 667 | }, 668 | { 669 | "internalType": "uint256", 670 | "name": "", 671 | "type": "uint256" 672 | } 673 | ], 674 | "name": "makerOrders", 675 | "outputs": [ 676 | { 677 | "internalType": "uint64", 678 | "name": "", 679 | "type": "uint64" 680 | } 681 | ], 682 | "stateMutability": "view", 683 | "type": "function" 684 | }, 685 | { 686 | "inputs": [ 687 | { 688 | "internalType": "uint64", 689 | "name": "id", 690 | "type": "uint64" 691 | } 692 | ], 693 | "name": "order", 694 | "outputs": [ 695 | { 696 | "components": [ 697 | { 698 | "internalType": "uint64", 699 | "name": "id", 700 | "type": "uint64" 701 | }, 702 | { 703 | "internalType": "uint32", 704 | "name": "status", 705 | "type": "uint32" 706 | }, 707 | { 708 | "internalType": "uint32", 709 | "name": "time", 710 | "type": "uint32" 711 | }, 712 | { 713 | "internalType": "uint32", 714 | "name": "filledTime", 715 | "type": "uint32" 716 | }, 717 | { 718 | "internalType": "uint256", 719 | "name": "srcFilledAmount", 720 | "type": "uint256" 721 | }, 722 | { 723 | "internalType": "address", 724 | "name": "maker", 725 | "type": "address" 726 | }, 727 | { 728 | "components": [ 729 | { 730 | "internalType": "address", 731 | "name": "exchange", 732 | "type": "address" 733 | }, 734 | { 735 | "internalType": "address", 736 | "name": "srcToken", 737 | "type": "address" 738 | }, 739 | { 740 | "internalType": "address", 741 | "name": "dstToken", 742 | "type": "address" 743 | }, 744 | { 745 | "internalType": "uint256", 746 | "name": "srcAmount", 747 | "type": "uint256" 748 | }, 749 | { 750 | "internalType": "uint256", 751 | "name": "srcBidAmount", 752 | "type": "uint256" 753 | }, 754 | { 755 | "internalType": "uint256", 756 | "name": "dstMinAmount", 757 | "type": "uint256" 758 | }, 759 | { 760 | "internalType": "uint32", 761 | "name": "deadline", 762 | "type": "uint32" 763 | }, 764 | { 765 | "internalType": "uint32", 766 | "name": "bidDelay", 767 | "type": "uint32" 768 | }, 769 | { 770 | "internalType": "uint32", 771 | "name": "fillDelay", 772 | "type": "uint32" 773 | }, 774 | { 775 | "internalType": "bytes", 776 | "name": "data", 777 | "type": "bytes" 778 | } 779 | ], 780 | "internalType": "struct OrderLib.Ask", 781 | "name": "ask", 782 | "type": "tuple" 783 | }, 784 | { 785 | "components": [ 786 | { 787 | "internalType": "uint32", 788 | "name": "time", 789 | "type": "uint32" 790 | }, 791 | { 792 | "internalType": "address", 793 | "name": "taker", 794 | "type": "address" 795 | }, 796 | { 797 | "internalType": "address", 798 | "name": "exchange", 799 | "type": "address" 800 | }, 801 | { 802 | "internalType": "uint256", 803 | "name": "dstAmount", 804 | "type": "uint256" 805 | }, 806 | { 807 | "internalType": "uint256", 808 | "name": "dstFee", 809 | "type": "uint256" 810 | }, 811 | { 812 | "internalType": "bytes", 813 | "name": "data", 814 | "type": "bytes" 815 | } 816 | ], 817 | "internalType": "struct OrderLib.Bid", 818 | "name": "bid", 819 | "type": "tuple" 820 | } 821 | ], 822 | "internalType": "struct OrderLib.Order", 823 | "name": "", 824 | "type": "tuple" 825 | } 826 | ], 827 | "stateMutability": "view", 828 | "type": "function" 829 | }, 830 | { 831 | "inputs": [ 832 | { 833 | "internalType": "address", 834 | "name": "maker", 835 | "type": "address" 836 | } 837 | ], 838 | "name": "orderIdsByMaker", 839 | "outputs": [ 840 | { 841 | "internalType": "uint64[]", 842 | "name": "", 843 | "type": "uint64[]" 844 | } 845 | ], 846 | "stateMutability": "view", 847 | "type": "function" 848 | }, 849 | { 850 | "inputs": [ 851 | { 852 | "internalType": "uint64", 853 | "name": "id", 854 | "type": "uint64" 855 | } 856 | ], 857 | "name": "prune", 858 | "outputs": [], 859 | "stateMutability": "nonpayable", 860 | "type": "function" 861 | }, 862 | { 863 | "inputs": [ 864 | { 865 | "internalType": "uint256", 866 | "name": "", 867 | "type": "uint256" 868 | } 869 | ], 870 | "name": "status", 871 | "outputs": [ 872 | { 873 | "internalType": "uint32", 874 | "name": "", 875 | "type": "uint32" 876 | } 877 | ], 878 | "stateMutability": "view", 879 | "type": "function" 880 | }, 881 | { 882 | "stateMutability": "payable", 883 | "type": "receive" 884 | } 885 | ] 886 | -------------------------------------------------------------------------------- /TOS.md: -------------------------------------------------------------------------------- 1 | # **TWAP PROTOCOL AND SMART CONTRACT TERMS OF USE** 2 | 3 | These Terms of Use (“Terms”) are a multi-party contract among you and all other participants in the current and future decentralized, blockchain-based protocol for executing Time Weighted Average Price trading strategies, initially developed by Orbs Ltd. (“Orbs”) and made available in open source code form at the GitHub repositories located at (the “Protocol”), which allows users to engage in certain complex trading strategies on decentralized exchanges (collectively, the “Services”). These Terms apply to all participants in the Protocol, including: (i) the individuals and entities that are the end-users of such applications or that otherwise interact directly or indirectly with the Protocol (“Users”); (ii) any exchange or trading platform on which the Protocol has been integrated or utilized to provide the Services on such exchange or trading platform (each, an “Exchange”); (iii) those participants interacting with the Protocol by providing bids in order to fill orders and execute transactions on the Protocol (each, a “Taker”), which takers may include the validator nodes (“Guardians”) who operate that certain decentralized blockchain infrastructure developed by Orbs (the “Orbs Network”), which now or in the future may provide certain functionality to the Protocol; and (v) Orbs and any and all entities and persons affiliated with Orbs, and their respective current or future subsidiaries, affiliates, directors, officers, employees, agents, representatives, and any other person or entity with any involvement in developing the Protocol ((i)-(v) collectively, the “Subject Persons”). Individually and collectively, each Subject Person will be referred to as “you” or “your.” Your use of and access to the Protocol and any Services or applications available through the Protocol, any financial transactions and any information, text, photos, graphics, videos, idea, concept, discovery, invention, development, technology, algorithm, program, code, documentation or other material or information available on or through the Protocol is subject to these Terms. 4 | 5 | These Terms are a legal agreement, and they contain important information on your legal rights, remedies and obligations. You may use the Protocol only if you can form a binding contract. 6 | 7 | Please read and review these Terms carefully. 8 | 9 | The Protocol is available only to individuals who (a) are at least eighteen (18) years old; and (b) possess the legal capacity to enter into these Terms (on behalf of themselves and their organization) and to form a binding agreement under any applicable law. You hereby represent that you possess the legal authority to enter into these Terms on your and your organization's behalf and to form a binding agreement under any applicable law, to use the Protocol in accordance with these Terms, and to perform fully your obligations hereunder. 10 | 11 | ### 1. **Overview of the Protocol and TWAP** 12 | 13 | The Protocol is a smart contract-based protocol that allows Users to receive the Services by creating orders that wait in the smart contract until they are filled by Takers and executed on one or more Exchanges. 14 | 15 | You hereby expressly acknowledge and agree that the Protocol is a decentralized software appplication for which no Subject Person: (A) is responsible for its operation; or (B) undertakes any responsibility to operate, maintain, support or otherwise authorize any activity related to the Protocol. 16 | 17 | ***THE PROTOCOL HAS BEEN MAD AVAILABLE ON AN "AS IS" BASIS, AND TO THE EXTENT LEGALLY PERMISSIBLE, ORBS WILL NOT BE LIABLE FOR ANY DAMAGES OR LOSS INCURRED TO YOU OR ANY OTHER PERSON AS A RESULT OF OR IN CONNECTION WITH YOUR USE OF THE PROTOCOL.*** 18 | 19 | ***YOUR USE OF THE PROTOCOL IS ENTIRELY AT YOUR OWN RISK.*** 20 | 21 | *Fees*. There is currently no fee for utilizing the Protocol or transactions conducted therein. However, fees may be charged in the future, and in such cases any applicable fees will be displayed prior to you incurring the payment(s). In addition, a variety of other fees may be imposed by various parties: (i) Takers may set fees in their bids, which will be payable by Users in the event that such bids are accepted; (ii) network fees (including, without limitation “miner’s fees”) required to use an Exchange or an underlying blockchain may apply to a transaction, which your third party wallet application may attempt to calculate such a fee for you, though it may not be sufficient or it may be excessive and ou may select a greater or lesser fee; and (iii) Orbs and/or one of the Exchanges may from time to time deploy “honest” bidders, which limit their fees to only the network fees incurred in filling your TWAP orders. You are solely responsible for selecting, if applicable, and paying any and all of hteabove-described categories of fees. 22 | 23 | *Be safe & secure*: If you elect to utilize the Protocol, it is highly recommended that you educate yourself regarding blockchain industry standard recommendations on how to be proactive about your security. 24 | 25 | * Subject Persons are not responsible for any loss*: The Protocol, the Exchanges, the assets traded thereon, any underlying blockchain network, and any third party wallet software (such as Metamask) that may be used by you, are all still under active development and may (a) contain bugs, errors and defects, (b) function improperly or be subject to periods of downtime and unavailability, (c) result in total or partial loss or corruption of data and (d) be modified at any time, including through the release of subsequent versions, all with or without notice to you. While the Protocol has been tested, there is always the possibility something happens that causes your assets to be lost. Please do not transact more than you are willing to lose, and please be careful. 26 | 27 | *Non-Custodial Protocol*. You do not create an account or give us your funds to hold. No data leaves your computer / your browser. At no point will the Protocol ever take custody of your digital assets, which will move directly from your wallet to the Exchange on which the trades are executed, and vice versa. The Protocol makes it easy for you to create and execute multiple orders and sophisticated strategies on one or more Exchanges. You acknowledge that neither the Protocol nor any Subject Person is responsible for transferring, safeguarding, or maintaining your private keys or any digital assets associated therewith. If you lose, mishandle or have stolen associated digital currency private keys, you acknowledge that you may not be able to recover associated digital currency, and that no Subject Person is responsible for any loss, damage or liability arising from your failure to comply with the terms hereunder. 28 | 29 | *Public Nature of Transactions*: Transactions that take place utilizing the Protocol are managed and confirmed via an underlying blockchain. You understand that your public address and the details of any order you create and any transactions executed on your behalf will be made publicly visible whenever you utilize the Protocol. 30 | 31 | *Your Resonsibility for Transactions*: You acknowledge that entering into any transaction electronically (including without limitation all records relating to such transactions) constitutes your agreement and intent to be bound by and to pay for such agreements and transactions. Such transactions may, among other things, include payments between participants for their activities on the network, fees paid to Bidders for executing your orders, fees paid to exchanges on which your transactions are executed and amounts paid by you in exchange for other digital assets as part of such transactions. Notwithstanding that such transactions and other similar transactions may be allowed, facilitated or encouraged by the design of the Protocol, you acknowledge and agree that the actual entry into any such agreement, transaction or arrangement utilizing the Protocol is accomplished as a peer-to-peer transaction between the parties thereto and is subject to the terms of the agreement between such parties. You are solely responsible for any disputes you may have with any counterparty or any other party regarding such agreements and transactions. SUBJECT PERSONS DISCLAIM ALL LIABILITY RELATED TO ANY DISPUTES BETWEEN, BY AND AMONG YOU AND ANY USERS, BIDDERS, EXCHANGE OR TRANSACTION COUNTERPARTY. 32 | 33 | *Dependence on Third Party Applications*: The Protocol is a smart contract launched on one or more underlying blockchain networks that takes orders for transactions that are executed with the assistance of Bidders by a connection between your wallet and liquidity pools available on an Exchange. All underlying blockchain networks, wallet software, Bidders and Exchanges are third parties or third party applications, as the case may be. No Subject Person owns or controls these or any other third-party site, product or service that you may choose to access, visit or use in the course or for the purpose of interacting with the Protocol. Accordingly, by using the Protocol, you acknowledge and agree that no Subject Person has control over, or any duty to take any action regarding, any activities of such third parties. No Subject Person makes any warranties or representations, express or implied, about any linked third party application, its ownership or operation, the information contained on such application or the suitability of such products or services. You acknoweldge sole responsibility for and assume all risk arsiing from your use of any third party website, applications or resources. 34 | 35 | *Risk Disclosures Regarding the Protocol*. By utilizing the Service or interacting with the Protocol in any way, you represent that you understand the inherent risks associated with cryptographic and blockchain systems; and warrant that you have an understanding of the usage and intricacies of cryptographic tokens, blockchain-based software systems and decentralized finance platforms such as the Exchanges, including, but not limited to, the following risks: 36 | 37 | A. In order to be completed, any transaction created with the Protocol must be confirmed and recorded on the applicable underlying blockchain. Such networks are decentralized, peer-to-peer networks operated by independent third parties, which are not owned, controlled or operated by any Subject Person. 38 | B. No Subject Person controls the actions of nodes on the underlying blockchains, any Bidders, or the Exchanges, and therefore cannot and do not ensure that any transactions you engage in via the Protocol will be confirmed or confirmed correctly on the relevant networks. The transaction details you submit via the Protocol may not be completed, or may be substantially delayed, by the applicable networks used to process the transaction. No Subject Person guarantees that the Protocol can transfer title or right in any digital assets or make any warranties whatsoever with regard to title. 39 | C. Once transaction details have been submitted via the Protocol, no Subject Person can assist you to cancel or otherwise modify your transaction or transaction details. 40 | D. The Protocol has been designed such that the presence of one “honest” Taker (i.e, a Taker who charges only reimbursement for gas fees) should result in an output amount that is as close as possible to spot market prices. While Orbs or other parties may implement automated “honest” Takers, it cannot be guaranteed that an “honest” taker will be available. All Takers are in beta and may not operate as intended. If an honest Taker is not available or fails to function as intended, your transaction may incur fees payable to the Taker offering the lowest fee rate in its bid to the Protocol with respect to your transaction. Such fees may fluctuate depending on Taker behavior. 41 | E. If you are using a user interface to interact with the Protocol, such user interface may display estimated information such as current market price, output tokens to be received in a TWAP order or other information with respect to your entered transactions. All such information displayed on a user interface may be inaccurate and such user interfaces may contain errors or defects. 42 | F. If you elect to engage in a “market order” using the Protocol, there is no set minimum number of digital assets you will receive in exchange for your digital assets and the actual results will depend on market conditions. 43 | G. If you elect to engage in a “limit order” using the Protocol, if the amount of digital assets receivable is below the limit set by you in any trade, such trades may not be executed. In such event, your order may only be partially filled. 44 | H. Use of the Protocol, in particular for trading digital assets, may carry financial risk. Digital assets are, by their nature, highly experimental, risky, and volatile. Transactions entered into in connection with the Protocol are irreversible, final and there are no refunds. You acknowledge and agree that you will access and use the Protocol and the Services at your own risk. The risk of loss in trading digital assets can be substantial. You should, therefore, carefully consider whether such trading is suitable for you in light of your circumstances and financial resources. By using the Protocol, you represent and warrant that you have been, are, and will be solely responsible for making your independent appraisal and investigations into the risks of a given transaction and the underlying digital assets. You represent that you have sufficient knowledge, market sophistication, professional advice, and experience to make your evaluation of the merits and risks of any transaction conducted in connection with the Protocol or any digital asset. You accept all consequences of using the Protocol, including the risk that you may lose access to your digital assets indefinitely. All transaction decisions are made solely by you. Notwithstanding anything in these Agreement, no Subject Person accepts any responsibility whatsoever for, and will in no circumstances be liable to you in connection with, your use of the Protocol for performing digital asset transactions. 45 | I. No financial regulatory authority has reviewed or approved the use of the Protocol or any of its related software. Use of the Protocol does not constitute advice or a recommendation concerning any commodity, security, or other digital asset or instrument. No Subject Person is acting as an investment adviser or commodity trading adviser to any person or entity. 46 | 47 | 48 | 49 | ### 2. **Prohibited Conduct** 50 | 51 | You warrant and agree that, while accessing or using the Protocol, you will not: 52 | 53 | - access, use or attempt to access or use another user’s account, including any private key, public key or wallet address, that you are not authorized to access or use; 54 | - interfere with or disrupt the operation of the Protocol or the servers or networks that host the Network, or disobey any laws, regulations, requirements, procedures, or policies of such servers or networks; 55 | - engage or attempt to engage in any attack or other malicious behavior or activity that threatens the integrity of the Protocol or any underlying blockchain, including, without limitation, 51% attacks, double-spend attacks, eclipse attacks, transaction malleability attacks, timejacking, routing attacks, Sybil attacks, denial of service attacks, or race condition attacks; 56 | - create a false identity for the purpose of misleading others or otherwise impersonate any person or entity or misrepresent your affiliation with any other person or entity, whether actual or fictitious, including any Subject Person or other participant in the Protocol; 57 | - transmit or otherwise make available in connection with the Protocol any virus, worm, Trojan Horse, time bomb, web bug, spyware, or any other computer code, file, or program that may or is intended to damage or hijack the operation of any hardware, software, or telecommunications equipment, or any other actually or potentially harmful, disruptive, or invasive code or component; 58 | - obtain or attempt to gain unauthorized access to other computer systems, materials, information available through the Protocol through any means, including through means not intentionally made publicly available or provided through the Protocol; 59 | - engage in any unauthorized means of accessing the Protocol, or obtaining lists of users or other information from or through the Protocol, including, without limitation, any information residing on any server or database connected to the Protocol; 60 | - use the Protocol in any manner that could interrupt, damage, disable, overburden or impair the Protocol, or interfere with any other party’s use of the Protocol, including, without limitation, sending mass unsolicited transactions or “flooding” servers; 61 | - use the Protocol in violation of proprietary or legal rights or rights of any party; 62 | - use the Protocol in violation of any applicable law; 63 | - use the Protocol if you are a citizen, resident or otherwise located in a country or region subject to comprehensive sanctions, including, but not limited to, the Crimean region of Ukraine, Cuba, Iran, Lebanon, North Korea and Syria; 64 | - - use the Protocol if you are a resident or citizen of any geographic area in which governmental authorities have indicated that the purchase or sale of digital assets may be prohibited by applicable law, decree, regulation, treaty or administrative act; 65 | - without derogating from the generality of any other provision of these Terms, use the Protocol for any type of activity associated with money laundering or terror financing, or to violate any provision of the U.S. Foreign Corrupt Practices Act of 1977, as amended, the UK Bribery Act (2010), Sections 290-297 of the Israeli Penal Law 1977 (Bribery Transactions), the Israeli Prohibition on Money Laundering Law, 2000, or any other applicable anti-corruption or anti bribery legislation; 66 | - use the Protocol if you are subject to any investigation by, or has received a request for information from any governmental body relating to corruption or bribery under any statute; 67 | - use the Protocol to engage in any conduct that is abusive, threatening, harmful, offensive, obscene, tortious or otherwise objectionable; or 68 | - attempt (or encourage or support anyone else’s attempt) to disable, bypass, modify, defeat, circumvent, reverse-engineer, decrypt, or otherwise alter, or interfere with, the Protocol, including without limitation any security, digital rights management or data encryption components limiting access to Protocol. 69 | 70 | ### 3. **Intellectual Property Rights** 71 | 72 | - The Protocol has been made available under the open-source MIT License, which can be viewed in its entirety here: https://github.com/orbs-network/twap/blob/master/LICENSE. 73 | 74 | - Ownership. The Protocol is licensed and not sold to you under this Agreement and you acknowledge that Orbs retains all title, ownership rights and Intellectual Property Rights (defined below) in and to the Protocol (and its related software). Orbs reserves all rights not expressly granted herein to the Protocol. “Intellectual Property Rights” means any and all rights in and to any and all trade secrets, patents, copyrights, service marks, trademarks, know-how, or similar intellectual property rights, as well as any and all moral rights, rights of privacy, publicity and similar rights of any type under the laws or regulations of any governmental, regulatory, or judicial authority, whether foreign or domestic. 75 | 76 | 77 | ### 4. **Third Party Open Source Software and Third Party Services** 78 | 79 | - Portions of the Protocol may include third party open source software that are subject to third party terms and conditions (“Third Party Terms”). If there is a conflict between any Third Party Terms and the terms of this Agreement, then the Third Party Terms shall prevail but solely in connection with the related third party open source software. Notwithstanding anything in this Agreement to the contrary, Orbs makes no warranty or indemnity hereunder with respect to any third party open source software. 80 | - When you use the Protocol, you may also be using the services of one or more third parties. Your use of those and other third-party services ("Third-Party Services") will be subject to the privacy policies, terms of use and similar policies and terms, and fees of those third party services. 81 | - The Protocol may contain links to Third-Party Services, and may leverage or plug into such Third Party Services to enable certain features. When using a Third Party Services, you understand that you are at no time transferring your assets to us. The Protocol provide access to Third Party Services only as a convenience, and no Subject Person has control over their content, no Subject Person warrants or endorses, or is responsible for the availability or legitimacy of, the content, products or services on or accessible from those Third Party Services (including any related website, resources or links displayed therein). No Subject Person makes anywarranties or representations, express or implied, about such linked Third Party Services, the third parties they are owned and operated by, the information contained on them or the suitability of their products or services. You acknowledge sole responsibility for and assume all risk arising from your use of any third-party website, applications, or resources. 82 | 83 | 84 | ### 5. **Representations and Warranties and Warranty Disclaimers** 85 | 86 | The Protocol is deployed on peer-to-peer platforms with a decentralized, blockchain-based architecture designed to provide the Services. As a decentralized protocol, the actual performance of the Protocol is dependent on the collective actions of various participants in the Protocol, including Exchanges and bidders. No party, including any Subject Person, can exert full control over the operation of the Protocol or the actual results obtained from its use. You acknowledge and agree that the control of the Protocol is distributed and that accordingly no particular party, including any Subject Person, is able to provide warranties or guarantees regarding the future operations or actual results of any utilizing on the Protocol. 87 | 88 | No Subject Person guarantees the accuracy, timeliness, correctness, completeness, performance or fitness for a particular purpose of the Protocol. No subject person is liable for any errors, omissions, or inaccuracies. Furthermore, the Subject Persons disclaim: (a) any and all responsibility or liability for the accuracy, content, completeness, legality, reliability, or availability or operability of information or materials displayed on any user interface used to interact with the Protocol; (b) any and all responsibility for the conduct of any user of the Services; (c) any and all responsibility for any harm resulting from utilizing the Protocol; (d) any and all responsibility for any financial gains, losses or tax consequences associated with the your use of the Services through the Protocol. 89 | 90 | WITHOUT LIMITING THE GENERALITY OF ANY OTHER PROVISION OF THE TERMS, THE PROTOCOL AND THE SERVICES ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, NONINFRINGEMENT AND THOSE ARISING FROM A COURSE OF DEALING OR USAGE OF TRADE. IN NO EVENT SHALL ANY SUBJECT PERSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE PROTOCOL SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 91 | 92 | NO PARTY WARRANTS THAT THE PROTOCOL OR THE SERVICES WILL BE TIMELY, SECURE, UNINTERRUPTED, OR OPERATED FREE OF DELAYS IN TRANSMISSION, FAILURE IN PERFORMANCE, COMPUTER VIRUSES, INACCURACIES, ERRORS, OR DEFECTS. YOU USE THE PROTOCOL AT YOUR OWN RISK. YOU ARE SOLELY RESPONSIBLE FOR IMPLEMENTING SUFFICIENT PROCEDURES AND VIRUS CHECKS (INCLUDING ANTI-VIRUS AND OTHER SECURITY CHECKS) TO SATISFY YOUR PARTICULAR REQUIREMENTS FOR THE ACCURACY AND SECURITY OF DATA INPUT AND OUTPUT. THERE IS NO WARRANTY THAT THE PROTOCOL WILL MEET USERS’ OR OTHER PARTICIPANTS’ REQUIREMENTS. NO ADVICE, RESULTS OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED BY YOU THROUGH THE PROTOCOL SHALL CREATE ANY WARRANTY NOT EXPRESSLY MADE HEREIN. 93 | 94 | Some jurisdictions do not allow the disclaimer of implied warranties, so the foregoing disclaimers may not apply to you. 95 | 96 | YOU ACKNOWLEDGE AND AGREE THAT YOUR SOLE REMEDY FOR ANY PROBLEMS OR DISSATISFACTION WITH THE PROTOCOL IS TO DISCONTINUE ALL USE OF THE PROTOCOL. 97 | 98 | ### 6. **Limitation of Liability** 99 | 100 | EXCEPT AS SET FORTH HEREIN, UNDER NO CIRCUMSTANCES, INCLUDING, WITHOUT LIMITATION, BREACH OF CONTRACT, TORT, NEGLIGENCE, STRICT LIABILITY OR ARISING UNDER ANY OTHER LEGAL OR EQUITABLE THEORY, WILL ANY SUBJECT PERSON BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL DAMAGES THAT ARISE OUT OF OR IN CONNECTION WITH THE USE OF THE PROTOCOL OR THE SERVICES OR FROM THE DELAY OR INABILITY TO USE THE PROTOCOL, OR FOR ANY PRODUCTS OR SERVICES OBTAINED OR TRANSACTIONS EXECUTED THROUGH THE PROTOCOL, OR FROM THE FAILURE OF THE PROTOCOL TO PERFORM AS DESCRIBED OR EXPECTED, OR FROM THE FAILURE OF ANY SUBJECT PERSON TO PERFORM UNDER THESE TERMS OR OTHERWISE ARISING OUT OF THE USE OF THE PROTOCOL, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. BECAUSE SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THOSE ASPECTS OF THE HEREIN LIMITATION MAY NOT APPLY TO YOU, IN WHICH CASE LIABILITY SHALL BE LIMITED TO THE FULLEST EXTENT PERMITTED BY LAW. 101 | 102 | IN NO EVENT SHALL THE TOTAL LIABILITY OF THE SUBJECT PERSONS TO YOU FOR ALL DAMAGES, LOSSES AND CAUSES OF ACTION EXCEED, IN THE AGGREGATE, ONE HUNDRED U.S. DOLLARS (US$100). 103 | 104 | 105 | ### 7. **Taxes** 106 | 107 | You hereby acknowledge and agree that you are responsible for all tax consequences of any transaction or activity you undertake using the Protocol, any provision or receipt of the Services, and/or any value received in connection therewith. You are solely responsible for determining whether or not to use the Protocol or to receive or provide any Services and you acknowledge that the Services may be subject to export restrictions and economic sanctions imposed by law. 108 | 109 | ### 8. **Updates and Upgrades** 110 | 111 | One or more Subject Person may from time to time provide updates or upgrades to the Protocol (each a "Revision"), but are not under any obligation to do so. Such Revisions will be supplied according to the then-current policies governing the Protocol, which may include automatic updating or upgrading without any additional notice to you. You hereby consent to any such automatic updating or upgrading of the Protocol. All references herein to the Protocol shall include Revisions. This Agreement shall govern any Revisions that replace or supplement the original Protocol, unless the Revision is accompanied by a separate license agreement which will govern the Revision and/or if otherwise instructed in writing by a Subject Person. 112 | 113 | ### 9. **Privacy** 114 | 115 | If you use the Protocol for its intended functionality and engage in transactions or take any actions in the Orbs Universe such as delegating or staking your Orbs Tokens, such actions and certain information related thereto will be recorded on the applicable underlying blockchain network on which such transaction or action is carried out, and, accordingly, information with respect to such transactions or actions will be publicly visible and may be freely collected and analyzed by any person or entity for any purpose. Many block chains are open to forensic analysis which can lead to deanonymization and the unintentional revelation of private financial information, especially when block chain data is combined with other data. Because these blockchains are decentralized or third-party networks that are not controlled or operated by any Subject Person, no Subject Person will be able to erase, modify, or alter any of your information from such networks. Please also be aware that certain information provided by you in connection with your use of the Protocol may be stored on your device (even if no Subject Person collects that information). You are solely responsible for maintaining the security of your device from unauthorized access. 116 | 117 | ### 10. **Export Laws** 118 | 119 | You agree to comply fully with all applicable export laws and regulations to ensure that neither the Protocol nor any technical data related thereto are exported or re-exported directly or indirectly in violation of, or used for any purposes prohibited by, such laws and regulations. 120 | 121 | ### 11. **Term and Termination** 122 | 123 | Your obligations and undertakings under this Agreement are effective until terminated by Orbs or by you. If you object to any term or condition of this Agreement or any subsequent modifications thereto, or become dissatisfied with the Protocol in any way, your only recourse and remedy (and the sole liability and/or obligation of any Subject Person) is to terminate your participation in this Agreement and discontinue immediately use of the Protocol and the Service. 124 | Upon any termination of your participation under this Agreement, you shall cease all use of the Protocol. Sections 3 (Intellectual Property Rights), ‎4 (Open Source Software), 5 (Warranty Disclaimers), 6 (Limitation of Liability), 8 (Privacy), and ‎13 (Assignment) to ‎15 (General) shall survive termination of this Agreement. 125 | 126 | 127 | ### 12. **Assignment** 128 | 129 | Your agreement to these Terms is personal to you and you may not transfer or assign it to any third party. 130 | 131 | ### 13. **Entire Agreement** 132 | 133 | YOU ACKNOWLEDGE THAT YOU HAVE READ THIS AGREEMENT, UNDERSTAND IT, AND AGREE TO BE BOUND BY ITS TERMS AND CONDITIONS. 134 | 135 | These Terms constitute the entire agreement among the Protocol participants and supersede all prior or contemporaneous communications and proposals, whether electronic, oral or written, between you and other Protocol participants, with respect to your use of the Protocol and all matters relating to your access to, and/or use of, the Protocol and the Services. A printed version of these Terms and of any notice given in electronic form shall be admissible in any and all judicial or administrative proceedings based upon or relating to these Terms to the same extent as other business documents and records originally generated and maintained in printed form. 136 | 137 | ### 14. **Governing Law & Disputes** 138 | 139 | Any dispute to which the Subject Persons are a party arising out of or related to the Protocol will be brought in, and you hereby consent to exclusive jurisdiction and venue of, the competent courts of the Tel-Aviv-Jaffa District, Israel, provided, however, that nothing herein shall be deemed to preclude the Subject Persons from bringing any suit, enforcing any right or taking other legal action in your local jurisdiction. You agree to waive all defenses of lack of personal jurisdiction and forum non-convenience and agree that process may be served in a manner authorized by applicable law or court rule. 140 | 141 | ### 15. **General** 142 | 143 | If any part of these Terms is determined to be invalid or unenforceable under applicable law including, without limitation, the warranty disclaimers and liability limitations stated herein, then the invalid or unenforceable provision(s) will be deemed superseded by a valid, enforceable provision that most closely matches the intent of the original provision, and the remainder of these Terms shall continue in full effect. 144 | 145 | As part of these Terms, all Exchanges and any other participant who facilitates the use of the Protocol, directly or indirectly, by any User, shall undertake to ensure that such User enters into an agreement or other arrangement which governs the relationship between such participant and such User regarding the Protocol and obligates such User to be subject to these Terms.  Such Exchange or other participant is solely responsible for the effectiveness of such arrangement with the applicable Users.  146 | 147 | --------------------------------------------------------------------------------