From ce89a7b4d8b075f935ee35000c264eb3d47b95e3 Mon Sep 17 00:00:00 2001 From: Astronaut828 Date: Wed, 1 May 2024 15:47:32 -0600 Subject: [PATCH] feat: :sparkles: MetalFunFactory incl. PriceCalc --- .gitmodules | 3 + .../8453/run-1713911060.json | 39 + .../8453/run-1713911111.json | 75 + .../8453/run-1713976930.json | 75 + .../8453/run-latest.json | 75 + .../11155111/run-1712342850.json | 318 ++ .../11155111/run-1712343176.json | 318 ++ foundry.toml | 5 + lib/solady | 1 + script/DeployMetalFunFactory.s.sol | 23 + script/DeployMetalFunFactoryV2.s.sol | 24 + script/DeployMetalFunToken.s.sol | 21 + script/DeployMetalFunTokenV2.s.sol | 21 + src/Constants.sol | 1 + src/MetalFunFactory.sol | 240 ++ src/MetalFunFactoryV2.sol | 200 ++ src/TokenFactory.sol | 3 + src/lib/TickMath.sol | 213 ++ src/lib/addresses.sol | 63 + src/lib/priceCalc.sol | 42 + test/e2e.t.sol | 52 +- test/e2e_v2.t.sol | 2 +- test/metal_funV2_e2e.t.sol | 31 + test/metal_fun_e2e.t.sol | 78 + test/mocks/UniswapV3Pool.sol | 3187 +++++++++++++++++ 25 files changed, 5058 insertions(+), 52 deletions(-) create mode 100644 broadcast/DeployMetalFunFactory.s.sol/8453/run-1713911060.json create mode 100644 broadcast/DeployMetalFunFactory.s.sol/8453/run-1713911111.json create mode 100644 broadcast/DeployMetalFunFactory.s.sol/8453/run-1713976930.json create mode 100644 broadcast/DeployMetalFunFactory.s.sol/8453/run-latest.json create mode 100644 broadcast/DeployTokenV2.s.sol/11155111/run-1712342850.json create mode 100644 broadcast/DeployTokenV2.s.sol/11155111/run-1712343176.json create mode 160000 lib/solady create mode 100644 script/DeployMetalFunFactory.s.sol create mode 100644 script/DeployMetalFunFactoryV2.s.sol create mode 100644 script/DeployMetalFunToken.s.sol create mode 100644 script/DeployMetalFunTokenV2.s.sol create mode 100644 src/MetalFunFactory.sol create mode 100644 src/MetalFunFactoryV2.sol create mode 100644 src/lib/TickMath.sol create mode 100644 src/lib/addresses.sol create mode 100644 src/lib/priceCalc.sol create mode 100644 test/metal_funV2_e2e.t.sol create mode 100644 test/metal_fun_e2e.t.sol create mode 100644 test/mocks/UniswapV3Pool.sol diff --git a/.gitmodules b/.gitmodules index 76acc5c..911b080 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/gaslitedrop"] path = lib/gaslitedrop url = https://github.com/PopPunkLLC/gaslitedrop +[submodule "lib/solady"] + path = lib/solady + url = https://github.com/vectorized/solady diff --git a/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713911060.json b/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713911060.json new file mode 100644 index 0000000..ecfe64a --- /dev/null +++ b/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713911060.json @@ -0,0 +1,39 @@ +{ + "transactions": [ + { + "hash": null, + "transactionType": "CREATE2", + "contractName": "MetalFunFactory", + "contractAddress": "0x4f91961dfe9e4bd04cd8e779017b1614dfb7050e", + "function": null, + "arguments": [ + "0x71e1BB6EA5B84E9Aa55691a1E86223d250a18F8F" + ], + "transaction": { + "from": "0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "gas": "0x160f72", + "value": "0x0", + "input": "0x32ab015add757ad0a0eefded4f6bec14f0edd9d43e2ab3010ba11251d716c28a6b02fbe6bb5084433a4b80000060805260e0604052600060a05273d74d14ebe305c93d023c966640788f05593f0fde60c0527fd74d14ebe305c93d023c966640788f05593f0fde00000000000000000000000060015534801561006157600080fd5b50604051611319380380611319833981016040819052610080916101c1565b806001600160a01b0381166100af57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6100b881610171565b5046600181148015906100cc575080600514155b80156100da57508061a4b114155b80156100e7575080600a14155b80156100f4575080608914155b8015610101575080603814155b801561010f57508061210514155b801561011e57508062014a3414155b801561012d57508062aa36a714155b801561013c5750806276adf114155b801561014c5750806327bc86aa14155b1561016a57604051636f0c900f60e01b815260040160405180910390fd5b50506101f1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156101d357600080fd5b81516001600160a01b03811681146101ea57600080fd5b9392505050565b6080516110f8610221600039600081816106a40152818161078b01528181610a430152610a6901526110f86000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806386b714e21161005b57806386b714e2146100f95780638da5cb5b14610145578063b548848714610160578063f2fde38b1461019257600080fd5b8063150b7a021461008d5780631b5b213c146100c9578063225b20b9146100de578063715018a6146100f1575b600080fd5b6100ab61009b366004610c2e565b630a85bd0160e11b949350505050565b6040516001600160e01b031990911681526020015b60405180910390f35b6100dc6100d7366004610cae565b6101a5565b005b6100dc6100ec366004610cd2565b6101d5565b6100dc6102ea565b60015461011e906001600160601b03811690600160601b90046001600160a01b031682565b604080516001600160601b0390931683526001600160a01b039091166020830152016100c0565b6000546040516001600160a01b0390911681526020016100c0565b61017361016e366004610dad565b6102fe565b604080516001600160a01b0390931683526020830191909152016100c0565b6100dc6101a0366004610cae565b610360565b6101ad6103a3565b600180546001600160a01b03909216600160601b026001600160601b03909216919091179055565b6101dd6103a3565b60006101e76103d0565b91505060005b82518110156102e457816001600160a01b031663fc6f7865604051806080016040528086858151811061022257610222610e11565b60209081029190910181015182526001600160a01b03808a16838301526001600160801b036040808501829052606094850182905280516001600160e01b031960e089901b1681528651600482015293860151909216602484015290840151811660448301529290910151909116606482015260840160408051808303816000875af11580156102b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102da9190610e27565b50506001016101ed565b50505050565b6102f26103a3565b6102fc60006105af565b565b60008061030b84846105ff565b6040519193509150339082906001600160a01b038516907f0d1d2eaa1e5bac93f8aaaed98c5de5ec54cdeb3867b3238f239475dfb0dd337d906103519089908990610e91565b60405180910390a49250929050565b6103686103a3565b6001600160a01b03811661039757604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6103a0816105af565b50565b6000546001600160a01b031633146102fc5760405163118cdaa760e01b815233600482015260240161038e565b600073c36442b4a4522e871399cd717abdd847ab11fe884660018190036104095773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc292505b806005036104295773b4fbf271143f4fbf7b91a5ded31805e42b2208d692505b8061a4b10361044a577382af49447d8a07e3bd95bd0d56f35241523fbab192505b80600a0361045d576006602160991b0192505b8060890361047d57730d500b1d8e8ef31e21c99d1db9a6444d3adf127092505b806038036104b45773bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c9250737b8a01b39d58278b5de7e48c8449c9f4f517061391505b80612105036104df576006602160991b0192507303a520b32c04bf3beef7beb72e919cf822ed34f191505b8062014a340361050b576006602160991b0192507327f971cb582bf9e50f397e4d29a5c7a34f11faa291505b8062aa36a7036105445773fff9976782d46cc05630d1f6ebab18b2324d6b149250731238536071e1c677a632429e3655c799b22cda5291505b806276adf103610570576006602160991b01925073bc91e8dfa3ff18de43853372a3d7dfe585137d7891505b806327bc86aa036105aa5773eb54dacb4c2ccb64f8074eceea33b5ebb38e538792507356c65e35f2dd06f659bcfe327c4d7f21c9b69c2f91505b509091565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060008061060d6103d0565b6040805180820182526001546001600160601b038116808352600160601b9091046001600160a01b03166020808401829052845146918101919091529384019190915293955091935060009261067b9160600160405160208183030381529060405280519060200120610927565b60405163148f51f360e01b81529092506001600160a01b0383169063148f51f3906106d09030907f0000000000000000000000000000000000000000000000000000000000000000908d908d90600401610ebf565b600060405180830381600087803b1580156106ea57600080fd5b505af11580156106fe573d6000803e3d6000fd5b5050600180549092508291506000906107219083906001600160601b0316610f19565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555050600080846001600160a01b0316836001600160a01b03161061076957848361076c565b82855b60405163095ea7b360e01b81526001600160a01b0387811660048301527f000000000000000000000000000000000000000000000000000000000000000060248301529294509092509084169063095ea7b3906044016020604051808303816000875af11580156107e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108059190610f40565b50600080610813858861099b565b6040516309f56ab160e11b81526001600160a01b038781166004830152868116602483015261271060448301528083166064830152929450909250908716906313ead562906084016020604051808303816000875af115801561087a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089e9190610f62565b50604051634418b22b60e11b81526000906001600160a01b038816906388316456906108ce908690600401610f7f565b6080604051808303816000875af11580156108ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109119190611043565b50979e919d50909b505050505050505050505050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b038116610995576040516330be1a3d60e21b815260040160405180910390fd5b92915050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052906001600160a01b0383811690851610818082610a11578587610a14565b86865b9150915060008084610a2b5760006202fcd8610a33565b6202fcd71960005b9150915060008086610a675760007f0000000000000000000000000000000000000000000000000000000000000000610a8b565b7f000000000000000000000000000000000000000000000000000000000000000060005b91509150604051806101600160405280876001600160a01b03168152602001866001600160a01b0316815260200161271062ffffff1681526020018560020b81526020018460020b8152602001838152602001828152602001670de0b6b3a764000084610af8919061108d565b610b0290856110af565b8152602001610b19670de0b6b3a76400008461108d565b610b2390846110af565b815230602082015242604090910152985086610b4d576d8253347bc8233800000000000000610b5a565b6a01f6dda7773495800000005b6dffffffffffffffffffffffffffff169750505050505050509250929050565b6001600160a01b03811681146103a057600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610bce57610bce610b8f565b604052919050565b600067ffffffffffffffff831115610bf057610bf0610b8f565b610c03601f8401601f1916602001610ba5565b9050828152838383011115610c1757600080fd5b828260208301376000602084830101529392505050565b60008060008060808587031215610c4457600080fd5b8435610c4f81610b7a565b93506020850135610c5f81610b7a565b925060408501359150606085013567ffffffffffffffff811115610c8257600080fd5b8501601f81018713610c9357600080fd5b610ca287823560208401610bd6565b91505092959194509250565b600060208284031215610cc057600080fd5b8135610ccb81610b7a565b9392505050565b60008060408385031215610ce557600080fd5b8235610cf081610b7a565b915060208381013567ffffffffffffffff80821115610d0e57600080fd5b818601915086601f830112610d2257600080fd5b813581811115610d3457610d34610b8f565b8060051b9150610d45848301610ba5565b8181529183018401918481019089841115610d5f57600080fd5b938501935b83851015610d7d57843582529385019390850190610d64565b8096505050505050509250929050565b600082601f830112610d9e57600080fd5b610ccb83833560208501610bd6565b60008060408385031215610dc057600080fd5b823567ffffffffffffffff80821115610dd857600080fd5b610de486838701610d8d565b93506020850135915080821115610dfa57600080fd5b50610e0785828601610d8d565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b60008060408385031215610e3a57600080fd5b505080516020909101519092909150565b6000815180845260005b81811015610e7157602081850181015186830182015201610e55565b506000602082860101526020601f19601f83011685010191505092915050565b604081526000610ea46040830185610e4b565b8281036020840152610eb68185610e4b565b95945050505050565b60018060a01b0385168152836020820152608060408201526000610ee66080830185610e4b565b8281036060840152610ef88185610e4b565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b6001600160601b03818116838216019080821115610f3957610f39610f03565b5092915050565b600060208284031215610f5257600080fd5b81518015158114610ccb57600080fd5b600060208284031215610f7457600080fd5b8151610ccb81610b7a565b81516001600160a01b0316815261016081016020830151610fab60208401826001600160a01b03169052565b506040830151610fc2604084018262ffffff169052565b506060830151610fd7606084018260020b9052565b506080830151610fec608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151611032828501826001600160a01b03169052565b505061014092830151919092015290565b6000806000806080858703121561105957600080fd5b8451935060208501516001600160801b038116811461107757600080fd5b6040860151606090960151949790965092505050565b6000826110aa57634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111561099557610995610f0356fea26469706673582212205ba53a5831e78c81dde8db30b101f11c54aecef988405887c4bba0c3aad2a1a664736f6c6343000819003300000000000000000000000071e1bb6ea5b84e9aa55691a1e86223d250a18f8f", + "nonce": "0x2", + "chainId": "0x2105", + "accessList": null, + "type": null + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [], + "libraries": [], + "pending": [], + "returns": { + "0": { + "internal_type": "contract MetalFunFactory", + "value": "0x4f91961DFE9E4bD04Cd8E779017B1614Dfb7050e" + } + }, + "timestamp": 1713911060, + "chain": 8453, + "commit": "6c27092" +} \ No newline at end of file diff --git a/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713911111.json b/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713911111.json new file mode 100644 index 0000000..41742f4 --- /dev/null +++ b/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713911111.json @@ -0,0 +1,75 @@ +{ + "transactions": [ + { + "hash": "0x74ab457a293327ba97dbc63d099cb0ba7cd7654a0bc8fcf0b30a58c9f6774fc4", + "transactionType": "CREATE2", + "contractName": "MetalFunFactory", + "contractAddress": "0x4f91961dfe9e4bd04cd8e779017b1614dfb7050e", + "function": null, + "arguments": [ + "0x71e1BB6EA5B84E9Aa55691a1E86223d250a18F8F" + ], + "transaction": { + "from": "0x711df55d3663c04b2cbced323ce4ece2cbab92b9", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "gas": "0x160f72", + "value": "0x0", + "input": "0x32ab015add757ad0a0eefded4f6bec14f0edd9d43e2ab3010ba11251d716c28a6b02fbe6bb5084433a4b80000060805260e0604052600060a05273d74d14ebe305c93d023c966640788f05593f0fde60c0527fd74d14ebe305c93d023c966640788f05593f0fde00000000000000000000000060015534801561006157600080fd5b50604051611319380380611319833981016040819052610080916101c1565b806001600160a01b0381166100af57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6100b881610171565b5046600181148015906100cc575080600514155b80156100da57508061a4b114155b80156100e7575080600a14155b80156100f4575080608914155b8015610101575080603814155b801561010f57508061210514155b801561011e57508062014a3414155b801561012d57508062aa36a714155b801561013c5750806276adf114155b801561014c5750806327bc86aa14155b1561016a57604051636f0c900f60e01b815260040160405180910390fd5b50506101f1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156101d357600080fd5b81516001600160a01b03811681146101ea57600080fd5b9392505050565b6080516110f8610221600039600081816106a40152818161078b01528181610a430152610a6901526110f86000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806386b714e21161005b57806386b714e2146100f95780638da5cb5b14610145578063b548848714610160578063f2fde38b1461019257600080fd5b8063150b7a021461008d5780631b5b213c146100c9578063225b20b9146100de578063715018a6146100f1575b600080fd5b6100ab61009b366004610c2e565b630a85bd0160e11b949350505050565b6040516001600160e01b031990911681526020015b60405180910390f35b6100dc6100d7366004610cae565b6101a5565b005b6100dc6100ec366004610cd2565b6101d5565b6100dc6102ea565b60015461011e906001600160601b03811690600160601b90046001600160a01b031682565b604080516001600160601b0390931683526001600160a01b039091166020830152016100c0565b6000546040516001600160a01b0390911681526020016100c0565b61017361016e366004610dad565b6102fe565b604080516001600160a01b0390931683526020830191909152016100c0565b6100dc6101a0366004610cae565b610360565b6101ad6103a3565b600180546001600160a01b03909216600160601b026001600160601b03909216919091179055565b6101dd6103a3565b60006101e76103d0565b91505060005b82518110156102e457816001600160a01b031663fc6f7865604051806080016040528086858151811061022257610222610e11565b60209081029190910181015182526001600160a01b03808a16838301526001600160801b036040808501829052606094850182905280516001600160e01b031960e089901b1681528651600482015293860151909216602484015290840151811660448301529290910151909116606482015260840160408051808303816000875af11580156102b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102da9190610e27565b50506001016101ed565b50505050565b6102f26103a3565b6102fc60006105af565b565b60008061030b84846105ff565b6040519193509150339082906001600160a01b038516907f0d1d2eaa1e5bac93f8aaaed98c5de5ec54cdeb3867b3238f239475dfb0dd337d906103519089908990610e91565b60405180910390a49250929050565b6103686103a3565b6001600160a01b03811661039757604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6103a0816105af565b50565b6000546001600160a01b031633146102fc5760405163118cdaa760e01b815233600482015260240161038e565b600073c36442b4a4522e871399cd717abdd847ab11fe884660018190036104095773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc292505b806005036104295773b4fbf271143f4fbf7b91a5ded31805e42b2208d692505b8061a4b10361044a577382af49447d8a07e3bd95bd0d56f35241523fbab192505b80600a0361045d576006602160991b0192505b8060890361047d57730d500b1d8e8ef31e21c99d1db9a6444d3adf127092505b806038036104b45773bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c9250737b8a01b39d58278b5de7e48c8449c9f4f517061391505b80612105036104df576006602160991b0192507303a520b32c04bf3beef7beb72e919cf822ed34f191505b8062014a340361050b576006602160991b0192507327f971cb582bf9e50f397e4d29a5c7a34f11faa291505b8062aa36a7036105445773fff9976782d46cc05630d1f6ebab18b2324d6b149250731238536071e1c677a632429e3655c799b22cda5291505b806276adf103610570576006602160991b01925073bc91e8dfa3ff18de43853372a3d7dfe585137d7891505b806327bc86aa036105aa5773eb54dacb4c2ccb64f8074eceea33b5ebb38e538792507356c65e35f2dd06f659bcfe327c4d7f21c9b69c2f91505b509091565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060008061060d6103d0565b6040805180820182526001546001600160601b038116808352600160601b9091046001600160a01b03166020808401829052845146918101919091529384019190915293955091935060009261067b9160600160405160208183030381529060405280519060200120610927565b60405163148f51f360e01b81529092506001600160a01b0383169063148f51f3906106d09030907f0000000000000000000000000000000000000000000000000000000000000000908d908d90600401610ebf565b600060405180830381600087803b1580156106ea57600080fd5b505af11580156106fe573d6000803e3d6000fd5b5050600180549092508291506000906107219083906001600160601b0316610f19565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555050600080846001600160a01b0316836001600160a01b03161061076957848361076c565b82855b60405163095ea7b360e01b81526001600160a01b0387811660048301527f000000000000000000000000000000000000000000000000000000000000000060248301529294509092509084169063095ea7b3906044016020604051808303816000875af11580156107e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108059190610f40565b50600080610813858861099b565b6040516309f56ab160e11b81526001600160a01b038781166004830152868116602483015261271060448301528083166064830152929450909250908716906313ead562906084016020604051808303816000875af115801561087a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089e9190610f62565b50604051634418b22b60e11b81526000906001600160a01b038816906388316456906108ce908690600401610f7f565b6080604051808303816000875af11580156108ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109119190611043565b50979e919d50909b505050505050505050505050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b038116610995576040516330be1a3d60e21b815260040160405180910390fd5b92915050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052906001600160a01b0383811690851610818082610a11578587610a14565b86865b9150915060008084610a2b5760006202fcd8610a33565b6202fcd71960005b9150915060008086610a675760007f0000000000000000000000000000000000000000000000000000000000000000610a8b565b7f000000000000000000000000000000000000000000000000000000000000000060005b91509150604051806101600160405280876001600160a01b03168152602001866001600160a01b0316815260200161271062ffffff1681526020018560020b81526020018460020b8152602001838152602001828152602001670de0b6b3a764000084610af8919061108d565b610b0290856110af565b8152602001610b19670de0b6b3a76400008461108d565b610b2390846110af565b815230602082015242604090910152985086610b4d576d8253347bc8233800000000000000610b5a565b6a01f6dda7773495800000005b6dffffffffffffffffffffffffffff169750505050505050509250929050565b6001600160a01b03811681146103a057600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610bce57610bce610b8f565b604052919050565b600067ffffffffffffffff831115610bf057610bf0610b8f565b610c03601f8401601f1916602001610ba5565b9050828152838383011115610c1757600080fd5b828260208301376000602084830101529392505050565b60008060008060808587031215610c4457600080fd5b8435610c4f81610b7a565b93506020850135610c5f81610b7a565b925060408501359150606085013567ffffffffffffffff811115610c8257600080fd5b8501601f81018713610c9357600080fd5b610ca287823560208401610bd6565b91505092959194509250565b600060208284031215610cc057600080fd5b8135610ccb81610b7a565b9392505050565b60008060408385031215610ce557600080fd5b8235610cf081610b7a565b915060208381013567ffffffffffffffff80821115610d0e57600080fd5b818601915086601f830112610d2257600080fd5b813581811115610d3457610d34610b8f565b8060051b9150610d45848301610ba5565b8181529183018401918481019089841115610d5f57600080fd5b938501935b83851015610d7d57843582529385019390850190610d64565b8096505050505050509250929050565b600082601f830112610d9e57600080fd5b610ccb83833560208501610bd6565b60008060408385031215610dc057600080fd5b823567ffffffffffffffff80821115610dd857600080fd5b610de486838701610d8d565b93506020850135915080821115610dfa57600080fd5b50610e0785828601610d8d565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b60008060408385031215610e3a57600080fd5b505080516020909101519092909150565b6000815180845260005b81811015610e7157602081850181015186830182015201610e55565b506000602082860101526020601f19601f83011685010191505092915050565b604081526000610ea46040830185610e4b565b8281036020840152610eb68185610e4b565b95945050505050565b60018060a01b0385168152836020820152608060408201526000610ee66080830185610e4b565b8281036060840152610ef88185610e4b565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b6001600160601b03818116838216019080821115610f3957610f39610f03565b5092915050565b600060208284031215610f5257600080fd5b81518015158114610ccb57600080fd5b600060208284031215610f7457600080fd5b8151610ccb81610b7a565b81516001600160a01b0316815261016081016020830151610fab60208401826001600160a01b03169052565b506040830151610fc2604084018262ffffff169052565b506060830151610fd7606084018260020b9052565b506080830151610fec608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151611032828501826001600160a01b03169052565b505061014092830151919092015290565b6000806000806080858703121561105957600080fd5b8451935060208501516001600160801b038116811461107757600080fd5b6040860151606090960151949790965092505050565b6000826110aa57634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111561099557610995610f0356fea26469706673582212205ba53a5831e78c81dde8db30b101f11c54aecef988405887c4bba0c3aad2a1a664736f6c6343000819003300000000000000000000000071e1bb6ea5b84e9aa55691a1e86223d250a18f8f", + "nonce": "0x52", + "chainId": "0x2105", + "accessList": null, + "type": null + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xa75b3b", + "logs": [ + { + "address": "0x4f91961dfe9e4bd04cd8e779017b1614dfb7050e", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000071e1bb6ea5b84e9aa55691a1e86223d250a18f8f" + ], + "data": "0x", + "blockHash": "0xe78fb67b597836e50b78fcdfbd5f6dfb1f8931075fc407ff5be64fb368dc5a1e", + "blockNumber": "0xceec26", + "transactionHash": "0x74ab457a293327ba97dbc63d099cb0ba7cd7654a0bc8fcf0b30a58c9f6774fc4", + "transactionIndex": "0x2f", + "logIndex": "0xb3", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000020000000000000000000000000000400000000000002000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000400000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000001000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x74ab457a293327ba97dbc63d099cb0ba7cd7654a0bc8fcf0b30a58c9f6774fc4", + "transactionIndex": "0x2f", + "blockHash": "0xe78fb67b597836e50b78fcdfbd5f6dfb1f8931075fc407ff5be64fb368dc5a1e", + "blockNumber": "0xceec26", + "gasUsed": "0xff9db", + "effectiveGasPrice": "0x3f4a1d1", + "from": "0x711df55d3663c04b2cbced323ce4ece2cbab92b9", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "contractAddress": "0x4f91961dfe9e4bd04cd8e779017b1614dfb7050e", + "l1Fee": "0xefc1e9ed33", + "l1GasPrice": "0x2d0796f68", + "l1GasUsed": "0x12e40" + } + ], + "libraries": [], + "pending": [], + "returns": { + "0": { + "internal_type": "contract MetalFunFactory", + "value": "0x4f91961DFE9E4bD04Cd8E779017B1614Dfb7050e" + } + }, + "timestamp": 1713911111, + "chain": 8453, + "commit": "6c27092" +} \ No newline at end of file diff --git a/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713976930.json b/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713976930.json new file mode 100644 index 0000000..3185b83 --- /dev/null +++ b/broadcast/DeployMetalFunFactory.s.sol/8453/run-1713976930.json @@ -0,0 +1,75 @@ +{ + "transactions": [ + { + "hash": "0x9f9bfc693d7588aba11898b537fa1254bd242ada52593435c65ae46563dac673", + "transactionType": "CREATE2", + "contractName": "MetalFunFactory", + "contractAddress": "0x773fd11afefbcbc7ef98fc51030d99c9f5605904", + "function": null, + "arguments": [ + "0x71e1BB6EA5B84E9Aa55691a1E86223d250a18F8F" + ], + "transaction": { + "from": "0x711df55d3663c04b2cbced323ce4ece2cbab92b9", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "gas": "0x160640", + "value": "0x0", + "input": "0x32ab015add757ad0a0eefded4f6bec14f0edd9d43e2ab3010ba11251d716c28a6b02fbe6bb5084433a4b80000060805260e0604052600060a05273d74d14ebe305c93d023c966640788f05593f0fde60c0527fd74d14ebe305c93d023c966640788f05593f0fde00000000000000000000000060015534801561006157600080fd5b50604051611311380380611311833981016040819052610080916101c1565b806001600160a01b0381166100af57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6100b881610171565b5046600181148015906100cc575080600514155b80156100da57508061a4b114155b80156100e7575080600a14155b80156100f4575080608914155b8015610101575080603814155b801561010f57508061210514155b801561011e57508062014a3414155b801561012d57508062aa36a714155b801561013c5750806276adf114155b801561014c5750806327bc86aa14155b1561016a57604051636f0c900f60e01b815260040160405180910390fd5b50506101f1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156101d357600080fd5b81516001600160a01b03811681146101ea57600080fd5b9392505050565b6080516110f0610221600039600081816106a40152818161078b01528181610a430152610a6901526110f06000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806386b714e21161005b57806386b714e2146100f95780638da5cb5b14610145578063b548848714610160578063f2fde38b1461019257600080fd5b8063150b7a021461008d5780631b5b213c146100c9578063225b20b9146100de578063715018a6146100f1575b600080fd5b6100ab61009b366004610c26565b630a85bd0160e11b949350505050565b6040516001600160e01b031990911681526020015b60405180910390f35b6100dc6100d7366004610ca6565b6101a5565b005b6100dc6100ec366004610cca565b6101d5565b6100dc6102ea565b60015461011e906001600160601b03811690600160601b90046001600160a01b031682565b604080516001600160601b0390931683526001600160a01b039091166020830152016100c0565b6000546040516001600160a01b0390911681526020016100c0565b61017361016e366004610da5565b6102fe565b604080516001600160a01b0390931683526020830191909152016100c0565b6100dc6101a0366004610ca6565b610360565b6101ad6103a3565b600180546001600160a01b03909216600160601b026001600160601b03909216919091179055565b6101dd6103a3565b60006101e76103d0565b91505060005b82518110156102e457816001600160a01b031663fc6f7865604051806080016040528086858151811061022257610222610e09565b60209081029190910181015182526001600160a01b03808a16838301526001600160801b036040808501829052606094850182905280516001600160e01b031960e089901b1681528651600482015293860151909216602484015290840151811660448301529290910151909116606482015260840160408051808303816000875af11580156102b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102da9190610e1f565b50506001016101ed565b50505050565b6102f26103a3565b6102fc60006105af565b565b60008061030b84846105ff565b6040519193509150339082906001600160a01b038516907f0d1d2eaa1e5bac93f8aaaed98c5de5ec54cdeb3867b3238f239475dfb0dd337d906103519089908990610e89565b60405180910390a49250929050565b6103686103a3565b6001600160a01b03811661039757604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6103a0816105af565b50565b6000546001600160a01b031633146102fc5760405163118cdaa760e01b815233600482015260240161038e565b600073c36442b4a4522e871399cd717abdd847ab11fe884660018190036104095773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc292505b806005036104295773b4fbf271143f4fbf7b91a5ded31805e42b2208d692505b8061a4b10361044a577382af49447d8a07e3bd95bd0d56f35241523fbab192505b80600a0361045d576006602160991b0192505b8060890361047d57730d500b1d8e8ef31e21c99d1db9a6444d3adf127092505b806038036104b45773bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c9250737b8a01b39d58278b5de7e48c8449c9f4f517061391505b80612105036104df576006602160991b0192507303a520b32c04bf3beef7beb72e919cf822ed34f191505b8062014a340361050b576006602160991b0192507327f971cb582bf9e50f397e4d29a5c7a34f11faa291505b8062aa36a7036105445773fff9976782d46cc05630d1f6ebab18b2324d6b149250731238536071e1c677a632429e3655c799b22cda5291505b806276adf103610570576006602160991b01925073bc91e8dfa3ff18de43853372a3d7dfe585137d7891505b806327bc86aa036105aa5773eb54dacb4c2ccb64f8074eceea33b5ebb38e538792507356c65e35f2dd06f659bcfe327c4d7f21c9b69c2f91505b509091565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060008061060d6103d0565b6040805180820182526001546001600160601b038116808352600160601b9091046001600160a01b03166020808401829052845146918101919091529384019190915293955091935060009261067b9160600160405160208183030381529060405280519060200120610927565b60405163148f51f360e01b81529092506001600160a01b0383169063148f51f3906106d09030907f0000000000000000000000000000000000000000000000000000000000000000908d908d90600401610eb7565b600060405180830381600087803b1580156106ea57600080fd5b505af11580156106fe573d6000803e3d6000fd5b5050600180549092508291506000906107219083906001600160601b0316610f11565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555050600080846001600160a01b0316836001600160a01b03161061076957848361076c565b82855b60405163095ea7b360e01b81526001600160a01b0387811660048301527f000000000000000000000000000000000000000000000000000000000000000060248301529294509092509084169063095ea7b3906044016020604051808303816000875af11580156107e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108059190610f38565b50600080610813858861099b565b6040516309f56ab160e11b81526001600160a01b038781166004830152868116602483015261271060448301528083166064830152929450909250908716906313ead562906084016020604051808303816000875af115801561087a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089e9190610f5a565b50604051634418b22b60e11b81526000906001600160a01b038816906388316456906108ce908690600401610f77565b6080604051808303816000875af11580156108ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610911919061103b565b50979e919d50909b505050505050505050505050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b038116610995576040516330be1a3d60e21b815260040160405180910390fd5b92915050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052906001600160a01b0383811690851610818082610a11578587610a14565b86865b9150915060008084610a2b57600062032e10610a33565b62032e0f1960005b9150915060008086610a675760007f0000000000000000000000000000000000000000000000000000000000000000610a8b565b7f000000000000000000000000000000000000000000000000000000000000000060005b91509150604051806101600160405280876001600160a01b03168152602001866001600160a01b0316815260200161271062ffffff1681526020018560020b81526020018460020b81526020018381526020018281526020016305f5e10084610af49190611085565b610afe90856110a7565b8152602001610b116305f5e10084611085565b610b1b90846110a7565b815230602082015242604090910152985086610b45576d82f0137f122ce800000000000000610b52565b6a01f4833103afd3d00000005b6dffffffffffffffffffffffffffff169750505050505050509250929050565b6001600160a01b03811681146103a057600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610bc657610bc6610b87565b604052919050565b600067ffffffffffffffff831115610be857610be8610b87565b610bfb601f8401601f1916602001610b9d565b9050828152838383011115610c0f57600080fd5b828260208301376000602084830101529392505050565b60008060008060808587031215610c3c57600080fd5b8435610c4781610b72565b93506020850135610c5781610b72565b925060408501359150606085013567ffffffffffffffff811115610c7a57600080fd5b8501601f81018713610c8b57600080fd5b610c9a87823560208401610bce565b91505092959194509250565b600060208284031215610cb857600080fd5b8135610cc381610b72565b9392505050565b60008060408385031215610cdd57600080fd5b8235610ce881610b72565b915060208381013567ffffffffffffffff80821115610d0657600080fd5b818601915086601f830112610d1a57600080fd5b813581811115610d2c57610d2c610b87565b8060051b9150610d3d848301610b9d565b8181529183018401918481019089841115610d5757600080fd5b938501935b83851015610d7557843582529385019390850190610d5c565b8096505050505050509250929050565b600082601f830112610d9657600080fd5b610cc383833560208501610bce565b60008060408385031215610db857600080fd5b823567ffffffffffffffff80821115610dd057600080fd5b610ddc86838701610d85565b93506020850135915080821115610df257600080fd5b50610dff85828601610d85565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b60008060408385031215610e3257600080fd5b505080516020909101519092909150565b6000815180845260005b81811015610e6957602081850181015186830182015201610e4d565b506000602082860101526020601f19601f83011685010191505092915050565b604081526000610e9c6040830185610e43565b8281036020840152610eae8185610e43565b95945050505050565b60018060a01b0385168152836020820152608060408201526000610ede6080830185610e43565b8281036060840152610ef08185610e43565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b6001600160601b03818116838216019080821115610f3157610f31610efb565b5092915050565b600060208284031215610f4a57600080fd5b81518015158114610cc357600080fd5b600060208284031215610f6c57600080fd5b8151610cc381610b72565b81516001600160a01b0316815261016081016020830151610fa360208401826001600160a01b03169052565b506040830151610fba604084018262ffffff169052565b506060830151610fcf606084018260020b9052565b506080830151610fe4608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e08301526101008084015181840152506101208084015161102a828501826001600160a01b03169052565b505061014092830151919092015290565b6000806000806080858703121561105157600080fd5b8451935060208501516001600160801b038116811461106f57600080fd5b6040860151606090960151949790965092505050565b6000826110a257634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111561099557610995610efb56fea26469706673582212204249de01420d07751e04eafcdd4b0f8568a682ed196745f46cbe942e1b6ce76864736f6c6343000819003300000000000000000000000071e1bb6ea5b84e9aa55691a1e86223d250a18f8f", + "nonce": "0x59", + "chainId": "0x2105", + "accessList": null, + "type": null + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x5c3b8e", + "logs": [ + { + "address": "0x773fd11afefbcbc7ef98fc51030d99c9f5605904", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000071e1bb6ea5b84e9aa55691a1e86223d250a18f8f" + ], + "data": "0x", + "blockHash": "0xf15997d026765f14512d86cb6ec2a6dab00c04ae393f959b3b3c7f053fb29c68", + "blockNumber": "0xcf6cb4", + "transactionHash": "0x9f9bfc693d7588aba11898b537fa1254bd242ada52593435c65ae46563dac673", + "transactionIndex": "0x1f", + "logIndex": "0x7c", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000020000000000000000000000000000400000000000002000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x9f9bfc693d7588aba11898b537fa1254bd242ada52593435c65ae46563dac673", + "transactionIndex": "0x1f", + "blockHash": "0xf15997d026765f14512d86cb6ec2a6dab00c04ae393f959b3b3c7f053fb29c68", + "blockNumber": "0xcf6cb4", + "gasUsed": "0xff333", + "effectiveGasPrice": "0xbeffb87", + "from": "0x711df55d3663c04b2cbced323ce4ece2cbab92b9", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "contractAddress": "0x773fd11afefbcbc7ef98fc51030d99c9f5605904", + "l1Fee": "0x1801108c3e4", + "l1GasPrice": "0x483ada63d", + "l1GasUsed": "0x12dd8" + } + ], + "libraries": [], + "pending": [], + "returns": { + "0": { + "internal_type": "contract MetalFunFactory", + "value": "0x773fd11aFeFbcBc7EF98fC51030D99C9f5605904" + } + }, + "timestamp": 1713976930, + "chain": 8453, + "commit": "5cb525f" +} \ No newline at end of file diff --git a/broadcast/DeployMetalFunFactory.s.sol/8453/run-latest.json b/broadcast/DeployMetalFunFactory.s.sol/8453/run-latest.json new file mode 100644 index 0000000..3185b83 --- /dev/null +++ b/broadcast/DeployMetalFunFactory.s.sol/8453/run-latest.json @@ -0,0 +1,75 @@ +{ + "transactions": [ + { + "hash": "0x9f9bfc693d7588aba11898b537fa1254bd242ada52593435c65ae46563dac673", + "transactionType": "CREATE2", + "contractName": "MetalFunFactory", + "contractAddress": "0x773fd11afefbcbc7ef98fc51030d99c9f5605904", + "function": null, + "arguments": [ + "0x71e1BB6EA5B84E9Aa55691a1E86223d250a18F8F" + ], + "transaction": { + "from": "0x711df55d3663c04b2cbced323ce4ece2cbab92b9", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "gas": "0x160640", + "value": "0x0", + "input": "0x32ab015add757ad0a0eefded4f6bec14f0edd9d43e2ab3010ba11251d716c28a6b02fbe6bb5084433a4b80000060805260e0604052600060a05273d74d14ebe305c93d023c966640788f05593f0fde60c0527fd74d14ebe305c93d023c966640788f05593f0fde00000000000000000000000060015534801561006157600080fd5b50604051611311380380611311833981016040819052610080916101c1565b806001600160a01b0381166100af57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6100b881610171565b5046600181148015906100cc575080600514155b80156100da57508061a4b114155b80156100e7575080600a14155b80156100f4575080608914155b8015610101575080603814155b801561010f57508061210514155b801561011e57508062014a3414155b801561012d57508062aa36a714155b801561013c5750806276adf114155b801561014c5750806327bc86aa14155b1561016a57604051636f0c900f60e01b815260040160405180910390fd5b50506101f1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156101d357600080fd5b81516001600160a01b03811681146101ea57600080fd5b9392505050565b6080516110f0610221600039600081816106a40152818161078b01528181610a430152610a6901526110f06000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806386b714e21161005b57806386b714e2146100f95780638da5cb5b14610145578063b548848714610160578063f2fde38b1461019257600080fd5b8063150b7a021461008d5780631b5b213c146100c9578063225b20b9146100de578063715018a6146100f1575b600080fd5b6100ab61009b366004610c26565b630a85bd0160e11b949350505050565b6040516001600160e01b031990911681526020015b60405180910390f35b6100dc6100d7366004610ca6565b6101a5565b005b6100dc6100ec366004610cca565b6101d5565b6100dc6102ea565b60015461011e906001600160601b03811690600160601b90046001600160a01b031682565b604080516001600160601b0390931683526001600160a01b039091166020830152016100c0565b6000546040516001600160a01b0390911681526020016100c0565b61017361016e366004610da5565b6102fe565b604080516001600160a01b0390931683526020830191909152016100c0565b6100dc6101a0366004610ca6565b610360565b6101ad6103a3565b600180546001600160a01b03909216600160601b026001600160601b03909216919091179055565b6101dd6103a3565b60006101e76103d0565b91505060005b82518110156102e457816001600160a01b031663fc6f7865604051806080016040528086858151811061022257610222610e09565b60209081029190910181015182526001600160a01b03808a16838301526001600160801b036040808501829052606094850182905280516001600160e01b031960e089901b1681528651600482015293860151909216602484015290840151811660448301529290910151909116606482015260840160408051808303816000875af11580156102b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102da9190610e1f565b50506001016101ed565b50505050565b6102f26103a3565b6102fc60006105af565b565b60008061030b84846105ff565b6040519193509150339082906001600160a01b038516907f0d1d2eaa1e5bac93f8aaaed98c5de5ec54cdeb3867b3238f239475dfb0dd337d906103519089908990610e89565b60405180910390a49250929050565b6103686103a3565b6001600160a01b03811661039757604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6103a0816105af565b50565b6000546001600160a01b031633146102fc5760405163118cdaa760e01b815233600482015260240161038e565b600073c36442b4a4522e871399cd717abdd847ab11fe884660018190036104095773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc292505b806005036104295773b4fbf271143f4fbf7b91a5ded31805e42b2208d692505b8061a4b10361044a577382af49447d8a07e3bd95bd0d56f35241523fbab192505b80600a0361045d576006602160991b0192505b8060890361047d57730d500b1d8e8ef31e21c99d1db9a6444d3adf127092505b806038036104b45773bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c9250737b8a01b39d58278b5de7e48c8449c9f4f517061391505b80612105036104df576006602160991b0192507303a520b32c04bf3beef7beb72e919cf822ed34f191505b8062014a340361050b576006602160991b0192507327f971cb582bf9e50f397e4d29a5c7a34f11faa291505b8062aa36a7036105445773fff9976782d46cc05630d1f6ebab18b2324d6b149250731238536071e1c677a632429e3655c799b22cda5291505b806276adf103610570576006602160991b01925073bc91e8dfa3ff18de43853372a3d7dfe585137d7891505b806327bc86aa036105aa5773eb54dacb4c2ccb64f8074eceea33b5ebb38e538792507356c65e35f2dd06f659bcfe327c4d7f21c9b69c2f91505b509091565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060008061060d6103d0565b6040805180820182526001546001600160601b038116808352600160601b9091046001600160a01b03166020808401829052845146918101919091529384019190915293955091935060009261067b9160600160405160208183030381529060405280519060200120610927565b60405163148f51f360e01b81529092506001600160a01b0383169063148f51f3906106d09030907f0000000000000000000000000000000000000000000000000000000000000000908d908d90600401610eb7565b600060405180830381600087803b1580156106ea57600080fd5b505af11580156106fe573d6000803e3d6000fd5b5050600180549092508291506000906107219083906001600160601b0316610f11565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555050600080846001600160a01b0316836001600160a01b03161061076957848361076c565b82855b60405163095ea7b360e01b81526001600160a01b0387811660048301527f000000000000000000000000000000000000000000000000000000000000000060248301529294509092509084169063095ea7b3906044016020604051808303816000875af11580156107e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108059190610f38565b50600080610813858861099b565b6040516309f56ab160e11b81526001600160a01b038781166004830152868116602483015261271060448301528083166064830152929450909250908716906313ead562906084016020604051808303816000875af115801561087a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089e9190610f5a565b50604051634418b22b60e11b81526000906001600160a01b038816906388316456906108ce908690600401610f77565b6080604051808303816000875af11580156108ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610911919061103b565b50979e919d50909b505050505050505050505050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b038116610995576040516330be1a3d60e21b815260040160405180910390fd5b92915050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052906001600160a01b0383811690851610818082610a11578587610a14565b86865b9150915060008084610a2b57600062032e10610a33565b62032e0f1960005b9150915060008086610a675760007f0000000000000000000000000000000000000000000000000000000000000000610a8b565b7f000000000000000000000000000000000000000000000000000000000000000060005b91509150604051806101600160405280876001600160a01b03168152602001866001600160a01b0316815260200161271062ffffff1681526020018560020b81526020018460020b81526020018381526020018281526020016305f5e10084610af49190611085565b610afe90856110a7565b8152602001610b116305f5e10084611085565b610b1b90846110a7565b815230602082015242604090910152985086610b45576d82f0137f122ce800000000000000610b52565b6a01f4833103afd3d00000005b6dffffffffffffffffffffffffffff169750505050505050509250929050565b6001600160a01b03811681146103a057600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610bc657610bc6610b87565b604052919050565b600067ffffffffffffffff831115610be857610be8610b87565b610bfb601f8401601f1916602001610b9d565b9050828152838383011115610c0f57600080fd5b828260208301376000602084830101529392505050565b60008060008060808587031215610c3c57600080fd5b8435610c4781610b72565b93506020850135610c5781610b72565b925060408501359150606085013567ffffffffffffffff811115610c7a57600080fd5b8501601f81018713610c8b57600080fd5b610c9a87823560208401610bce565b91505092959194509250565b600060208284031215610cb857600080fd5b8135610cc381610b72565b9392505050565b60008060408385031215610cdd57600080fd5b8235610ce881610b72565b915060208381013567ffffffffffffffff80821115610d0657600080fd5b818601915086601f830112610d1a57600080fd5b813581811115610d2c57610d2c610b87565b8060051b9150610d3d848301610b9d565b8181529183018401918481019089841115610d5757600080fd5b938501935b83851015610d7557843582529385019390850190610d5c565b8096505050505050509250929050565b600082601f830112610d9657600080fd5b610cc383833560208501610bce565b60008060408385031215610db857600080fd5b823567ffffffffffffffff80821115610dd057600080fd5b610ddc86838701610d85565b93506020850135915080821115610df257600080fd5b50610dff85828601610d85565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b60008060408385031215610e3257600080fd5b505080516020909101519092909150565b6000815180845260005b81811015610e6957602081850181015186830182015201610e4d565b506000602082860101526020601f19601f83011685010191505092915050565b604081526000610e9c6040830185610e43565b8281036020840152610eae8185610e43565b95945050505050565b60018060a01b0385168152836020820152608060408201526000610ede6080830185610e43565b8281036060840152610ef08185610e43565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b6001600160601b03818116838216019080821115610f3157610f31610efb565b5092915050565b600060208284031215610f4a57600080fd5b81518015158114610cc357600080fd5b600060208284031215610f6c57600080fd5b8151610cc381610b72565b81516001600160a01b0316815261016081016020830151610fa360208401826001600160a01b03169052565b506040830151610fba604084018262ffffff169052565b506060830151610fcf606084018260020b9052565b506080830151610fe4608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e08301526101008084015181840152506101208084015161102a828501826001600160a01b03169052565b505061014092830151919092015290565b6000806000806080858703121561105157600080fd5b8451935060208501516001600160801b038116811461106f57600080fd5b6040860151606090960151949790965092505050565b6000826110a257634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111561099557610995610efb56fea26469706673582212204249de01420d07751e04eafcdd4b0f8568a682ed196745f46cbe942e1b6ce76864736f6c6343000819003300000000000000000000000071e1bb6ea5b84e9aa55691a1e86223d250a18f8f", + "nonce": "0x59", + "chainId": "0x2105", + "accessList": null, + "type": null + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x5c3b8e", + "logs": [ + { + "address": "0x773fd11afefbcbc7ef98fc51030d99c9f5605904", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000071e1bb6ea5b84e9aa55691a1e86223d250a18f8f" + ], + "data": "0x", + "blockHash": "0xf15997d026765f14512d86cb6ec2a6dab00c04ae393f959b3b3c7f053fb29c68", + "blockNumber": "0xcf6cb4", + "transactionHash": "0x9f9bfc693d7588aba11898b537fa1254bd242ada52593435c65ae46563dac673", + "transactionIndex": "0x1f", + "logIndex": "0x7c", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000020000000000000000000000000000400000000000002000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x9f9bfc693d7588aba11898b537fa1254bd242ada52593435c65ae46563dac673", + "transactionIndex": "0x1f", + "blockHash": "0xf15997d026765f14512d86cb6ec2a6dab00c04ae393f959b3b3c7f053fb29c68", + "blockNumber": "0xcf6cb4", + "gasUsed": "0xff333", + "effectiveGasPrice": "0xbeffb87", + "from": "0x711df55d3663c04b2cbced323ce4ece2cbab92b9", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "contractAddress": "0x773fd11afefbcbc7ef98fc51030d99c9f5605904", + "l1Fee": "0x1801108c3e4", + "l1GasPrice": "0x483ada63d", + "l1GasUsed": "0x12dd8" + } + ], + "libraries": [], + "pending": [], + "returns": { + "0": { + "internal_type": "contract MetalFunFactory", + "value": "0x773fd11aFeFbcBc7EF98fC51030D99C9f5605904" + } + }, + "timestamp": 1713976930, + "chain": 8453, + "commit": "5cb525f" +} \ No newline at end of file diff --git a/broadcast/DeployTokenV2.s.sol/11155111/run-1712342850.json b/broadcast/DeployTokenV2.s.sol/11155111/run-1712342850.json new file mode 100644 index 0000000..96c9360 --- /dev/null +++ b/broadcast/DeployTokenV2.s.sol/11155111/run-1712342850.json @@ -0,0 +1,318 @@ +{ + "transactions": [ + { + "hash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xcE2c49b75fF7a0C5f45EffDB2b6A776e60A0deCb", + "function": "deployWithAirdrop(string,string,address[])", + "arguments": [ + "\"\"", + "\"\"", + "[0x0000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000003]" + ], + "transaction": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "gas": "0x718656", + "value": "0x0", + "data": "0x34967f580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + "nonce": "0x1526", + "accessList": [] + }, + "additionalContracts": [ + { + "transactionType": "CREATE2", + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "initCode": "0x3d602d80600a3d3981f3363d3d373d3d3d363d73d74d14ebe305c93d023c966640788f05593f0fde5af43d82803e903d91602b57fd5bf3" + }, + { + "transactionType": "CREATE2", + "address": "0x9CBAc168ddA0f817D1AFA06BCa721902AC8C364e", + "initCode": "0x6101606040523480156200001257600080fd5b503060601b60805260408051630890357360e41b81529051600091339163890357309160048082019260a092909190829003018186803b1580156200005657600080fd5b505afa1580156200006b573d6000803e3d6000fd5b505050506040513d60a08110156200008257600080fd5b508051602080830151604084015160608086015160809096015160e896871b6001600160e81b0319166101005291811b6001600160601b031990811660e05292811b831660c0529390931b1660a052600282810b900b90921b610120529150620000f79082906200010f811b62002b8417901c565b60801b6001600160801b03191661014052506200017d565b60008082600281900b620d89e719816200012557fe5b05029050600083600281900b620d89e8816200013d57fe5b0502905060008460020b83830360020b816200015557fe5b0560010190508062ffffff166001600160801b038016816200017357fe5b0495945050505050565b60805160601c60a05160601c60c05160601c60e05160601c6101005160e81c6101205160e81c6101405160801c61567e6200024a60003980611fee5280614b5f5280614b96525080610c0052806128fd5280614bca5280614bfc525080610cef52806119cb5280611a0252806129455250806111c75280611a855280611ef4528061244452806129215280613e6b5250806108d252806112f55280611a545280611e8e52806123be5280613d2252508061207b528061227d52806128d9525080612bfb525061567e6000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806370cf754a116100ee578063c45a015511610097578063ddca3f4311610071578063ddca3f4314610800578063f305839914610820578063f30dba9314610828578063f637731d146108aa576101ae565b8063c45a0155146107d1578063d0c93a7c146107d9578063d21220a7146107f8576101ae565b8063883bdbfd116100c8578063883bdbfd14610633578063a34123a71461073c578063a38807f214610776576101ae565b806370cf754a146105c65780638206a4d1146105ce57806385b66729146105f6576101ae565b80633850c7bd1161015b578063490e6cbc11610135578063490e6cbc146104705780634f1eb3d8146104fc578063514ea4bf1461054d5780635339c296146105a6576101ae565b80633850c7bd1461035b5780633c8a7d8d146103b45780634614131914610456576101ae565b80631ad8b03b1161018c5780631ad8b03b146102aa578063252c09d7146102e157806332148f6714610338576101ae565b80630dfe1681146101b3578063128acb08146101d75780631a68650214610286575b600080fd5b6101bb6108d0565b604080516001600160a01b039092168252519081900360200190f35b61026d600480360360a08110156101ed57600080fd5b6001600160a01b0382358116926020810135151592604082013592606083013516919081019060a08101608082013564010000000081111561022e57600080fd5b82018360208201111561024057600080fd5b8035906020019184600183028401116401000000008311171561026257600080fd5b5090925090506108f4565b6040805192835260208301919091528051918290030190f35b61028e6114ad565b604080516001600160801b039092168252519081900360200190f35b6102b26114bc565b60405180836001600160801b03168152602001826001600160801b031681526020019250505060405180910390f35b6102fe600480360360208110156102f757600080fd5b50356114d6565b6040805163ffffffff909516855260069390930b60208501526001600160a01b039091168383015215156060830152519081900360800190f35b6103596004803603602081101561034e57600080fd5b503561ffff1661151c565b005b610363611616565b604080516001600160a01b03909816885260029690960b602088015261ffff9485168787015292841660608701529216608085015260ff90911660a0840152151560c0830152519081900360e00190f35b61026d600480360360a08110156103ca57600080fd5b6001600160a01b03823516916020810135600290810b92604083013590910b916001600160801b036060820135169181019060a08101608082013564010000000081111561041757600080fd5b82018360208201111561042957600080fd5b8035906020019184600183028401116401000000008311171561044b57600080fd5b509092509050611666565b61045e611922565b60408051918252519081900360200190f35b6103596004803603608081101561048657600080fd5b6001600160a01b0382351691602081013591604082013591908101906080810160608201356401000000008111156104bd57600080fd5b8201836020820111156104cf57600080fd5b803590602001918460018302840111640100000000831117156104f157600080fd5b509092509050611928565b6102b2600480360360a081101561051257600080fd5b506001600160a01b03813516906020810135600290810b91604081013590910b906001600160801b0360608201358116916080013516611d83565b61056a6004803603602081101561056357600080fd5b5035611f9d565b604080516001600160801b0396871681526020810195909552848101939093529084166060840152909216608082015290519081900360a00190f35b61045e600480360360208110156105bc57600080fd5b503560010b611fda565b61028e611fec565b610359600480360360408110156105e457600080fd5b5060ff81358116916020013516612010565b6102b26004803603606081101561060c57600080fd5b506001600160a01b03813516906001600160801b036020820135811691604001351661220f565b6106a36004803603602081101561064957600080fd5b81019060208101813564010000000081111561066457600080fd5b82018360208201111561067657600080fd5b8035906020019184602083028401116401000000008311171561069857600080fd5b5090925090506124dc565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156106e75781810151838201526020016106cf565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561072657818101518382015260200161070e565b5050505090500194505050505060405180910390f35b61026d6004803603606081101561075257600080fd5b508035600290810b91602081013590910b90604001356001600160801b0316612569565b6107a06004803603604081101561078c57600080fd5b508035600290810b9160200135900b6126e0565b6040805160069490940b84526001600160a01b03909216602084015263ffffffff1682820152519081900360600190f35b6101bb6128d7565b6107e16128fb565b6040805160029290920b8252519081900360200190f35b6101bb61291f565b610808612943565b6040805162ffffff9092168252519081900360200190f35b61045e612967565b6108486004803603602081101561083e57600080fd5b503560020b61296d565b604080516001600160801b039099168952600f9790970b602089015287870195909552606087019390935260069190910b60808601526001600160a01b031660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b610359600480360360208110156108c057600080fd5b50356001600160a01b03166129db565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806108ff612bf0565b85610936576040805162461bcd60e51b8152602060048201526002602482015261415360f01b604482015290519081900360640190fd5b6040805160e0810182526000546001600160a01b0381168252600160a01b8104600290810b810b900b602083015261ffff600160b81b8204811693830193909352600160c81b810483166060830152600160d81b8104909216608082015260ff600160e81b8304811660a0830152600160f01b909204909116151560c082018190526109ef576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b87610a3a5780600001516001600160a01b0316866001600160a01b0316118015610a35575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038716105b610a6c565b80600001516001600160a01b0316866001600160a01b0316108015610a6c57506401000276a36001600160a01b038716115b610aa3576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b6000805460ff60f01b191681556040805160c08101909152808a610ad25760048460a0015160ff16901c610ae5565b60108460a0015160ff1681610ae357fe5b065b60ff1681526004546001600160801b03166020820152604001610b06612c27565b63ffffffff168152602001600060060b815260200160006001600160a01b031681526020016000151581525090506000808913905060006040518060e001604052808b81526020016000815260200185600001516001600160a01b03168152602001856020015160020b81526020018c610b8257600254610b86565b6001545b815260200160006001600160801b0316815260200184602001516001600160801b031681525090505b805115801590610bd55750886001600160a01b031681604001516001600160a01b031614155b15610f9f57610be261560e565b60408201516001600160a01b031681526060820151610c25906006907f00000000000000000000000000000000000000000000000000000000000000008f612c2b565b15156040830152600290810b810b60208301819052620d89e719910b1215610c5657620d89e7196020820152610c75565b6020810151620d89e860029190910b1315610c7557620d89e860208201525b610c828160200151612d6d565b6001600160a01b031660608201526040820151610d13908d610cbc578b6001600160a01b031683606001516001600160a01b031611610cd6565b8b6001600160a01b031683606001516001600160a01b0316105b610ce4578260600151610ce6565b8b5b60c085015185517f000000000000000000000000000000000000000000000000000000000000000061309f565b60c085015260a084015260808301526001600160a01b031660408301528215610d7557610d498160c00151826080015101613291565b825103825260a0810151610d6b90610d6090613291565b6020840151906132a7565b6020830152610db0565b610d828160a00151613291565b825101825260c08101516080820151610daa91610d9f9101613291565b6020840151906132c3565b60208301525b835160ff1615610df6576000846000015160ff168260c0015181610dd057fe5b60c0840180519290910491829003905260a0840180519091016001600160801b03169052505b60c08201516001600160801b031615610e3557610e298160c00151600160801b8460c001516001600160801b03166132d9565b60808301805190910190525b80606001516001600160a01b031682604001516001600160a01b03161415610f5e57806040015115610f35578360a00151610ebf57610e9d846040015160008760200151886040015188602001518a606001516008613389909695949392919063ffffffff16565b6001600160a01b03166080860152600690810b900b6060850152600160a08501525b6000610f0b82602001518e610ed657600154610edc565b84608001515b8f610eeb578560800151610eef565b6002545b608089015160608a015160408b0151600595949392919061351c565b90508c15610f17576000035b610f258360c00151826135ef565b6001600160801b031660c0840152505b8b610f44578060200151610f4d565b60018160200151035b600290810b900b6060830152610f99565b80600001516001600160a01b031682604001516001600160a01b031614610f9957610f8c82604001516136a5565b600290810b900b60608301525b50610baf565b836020015160020b816060015160020b1461107a57600080610fed86604001518660400151886020015188602001518a606001518b6080015160086139d1909695949392919063ffffffff16565b604085015160608601516000805461ffff60c81b1916600160c81b61ffff958616021761ffff60b81b1916600160b81b95909416949094029290921762ffffff60a01b1916600160a01b62ffffff60029490940b93909316929092029190911773ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116179055506110ac9050565b60408101516000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039092169190911790555b8060c001516001600160801b031683602001516001600160801b0316146110f25760c0810151600480546001600160801b0319166001600160801b039092169190911790555b8a1561114257608081015160015560a08101516001600160801b03161561113d5760a0810151600380546001600160801b031981166001600160801b03918216909301169190911790555b611188565b608081015160025560a08101516001600160801b0316156111885760a0810151600380546001600160801b03808216600160801b92839004821690940116029190911790555b8115158b1515146111a157602081015181518b036111ae565b80600001518a0381602001515b90965094508a156112e75760008512156111f0576111f07f00000000000000000000000000000000000000000000000000000000000000008d87600003613b86565b60006111fa613cd4565b9050336001600160a01b031663fa461e3388888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561127e57600080fd5b505af1158015611292573d6000803e3d6000fd5b5050505061129e613cd4565b6112a88289613e0d565b11156112e1576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b50611411565b600086121561131e5761131e7f00000000000000000000000000000000000000000000000000000000000000008d88600003613b86565b6000611328613e1d565b9050336001600160a01b031663fa461e3388888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156113ac57600080fd5b505af11580156113c0573d6000803e3d6000fd5b505050506113cc613e1d565b6113d68288613e0d565b111561140f576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b60408082015160c083015160608085015184518b8152602081018b90526001600160a01b03948516818701526001600160801b039093169183019190915260020b60808201529151908e169133917fc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca679181900360a00190a350506000805460ff60f01b1916600160f01b17905550919890975095505050505050565b6004546001600160801b031681565b6003546001600160801b0380821691600160801b90041682565b60088161ffff81106114e757600080fd5b015463ffffffff81169150640100000000810460060b90600160581b81046001600160a01b031690600160f81b900460ff1684565b600054600160f01b900460ff16611560576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b19169055611575612bf0565b60008054600160d81b900461ffff169061159160088385613eb5565b6000805461ffff808416600160d81b810261ffff60d81b19909316929092179092559192508316146115fe576040805161ffff80851682528316602082015281517fac49e518f90a358f652e4400164f05a5d8f7e35e7747279bc3a93dbf584e125a929181900390910190a15b50506000805460ff60f01b1916600160f01b17905550565b6000546001600160a01b03811690600160a01b810460020b9061ffff600160b81b8204811691600160c81b8104821691600160d81b8204169060ff600160e81b8204811691600160f01b90041687565b600080548190600160f01b900460ff166116ad576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b191690556001600160801b0385166116cd57600080fd5b60008061171b60405180608001604052808c6001600160a01b031681526020018b60020b81526020018a60020b81526020016117118a6001600160801b0316613f58565b600f0b9052613f69565b9250925050819350809250600080600086111561173d5761173a613cd4565b91505b841561174e5761174b613e1d565b90505b336001600160a01b031663d348799787878b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156117d057600080fd5b505af11580156117e4573d6000803e3d6000fd5b50505050600086111561183b576117f9613cd4565b6118038388613e0d565b111561183b576040805162461bcd60e51b815260206004820152600260248201526104d360f41b604482015290519081900360640190fd5b841561188b57611849613e1d565b6118538287613e0d565b111561188b576040805162461bcd60e51b81526020600482015260026024820152614d3160f01b604482015290519081900360640190fd5b8960020b8b60020b8d6001600160a01b03167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde338d8b8b60405180856001600160a01b03168152602001846001600160801b0316815260200183815260200182815260200194505050505060405180910390a450506000805460ff60f01b1916600160f01b17905550919890975095505050505050565b60025481565b600054600160f01b900460ff1661196c576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b19169055611981612bf0565b6004546001600160801b0316806119c3576040805162461bcd60e51b81526020600482015260016024820152601360fa1b604482015290519081900360640190fd5b60006119f8867f000000000000000000000000000000000000000000000000000000000000000062ffffff16620f42406141a9565b90506000611a2f867f000000000000000000000000000000000000000000000000000000000000000062ffffff16620f42406141a9565b90506000611a3b613cd4565b90506000611a47613e1d565b90508815611a7a57611a7a7f00000000000000000000000000000000000000000000000000000000000000008b8b613b86565b8715611aab57611aab7f00000000000000000000000000000000000000000000000000000000000000008b8a613b86565b336001600160a01b031663e9cbafb085858a8a6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015611b2d57600080fd5b505af1158015611b41573d6000803e3d6000fd5b505050506000611b4f613cd4565b90506000611b5b613e1d565b905081611b688588613e0d565b1115611ba0576040805162461bcd60e51b8152602060048201526002602482015261046360f41b604482015290519081900360640190fd5b80611bab8487613e0d565b1115611be3576040805162461bcd60e51b8152602060048201526002602482015261463160f01b604482015290519081900360640190fd5b8382038382038115611c725760008054600160e81b9004600f16908115611c16578160ff168481611c1057fe5b04611c19565b60005b90506001600160801b03811615611c4c57600380546001600160801b038082168401166001600160801b03199091161790555b611c66818503600160801b8d6001600160801b03166132d9565b60018054909101905550505b8015611cfd5760008054600160e81b900460041c600f16908115611ca2578160ff168381611c9c57fe5b04611ca5565b60005b90506001600160801b03811615611cd757600380546001600160801b03600160801b8083048216850182160291161790555b611cf1818403600160801b8d6001600160801b03166132d9565b60028054909101905550505b8d6001600160a01b0316336001600160a01b03167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a350506000805460ff60f01b1916600160f01b179055505050505050505050505050565b600080548190600160f01b900460ff16611dca576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b19168155611de460073389896141e3565b60038101549091506001600160801b0390811690861611611e055784611e14565b60038101546001600160801b03165b60038201549093506001600160801b03600160801b909104811690851611611e3c5783611e52565b6003810154600160801b90046001600160801b03165b91506001600160801b03831615611eb7576003810180546001600160801b031981166001600160801b03918216869003821617909155611eb7907f0000000000000000000000000000000000000000000000000000000000000000908a908616613b86565b6001600160801b03821615611f1d576003810180546001600160801b03600160801b808304821686900382160291811691909117909155611f1d907f0000000000000000000000000000000000000000000000000000000000000000908a908516613b86565b604080516001600160a01b038a1681526001600160801b0380861660208301528416818301529051600288810b92908a900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a4506000805460ff60f01b1916600160f01b17905590969095509350505050565b60076020526000908152604090208054600182015460028301546003909301546001600160801b0392831693919281811691600160801b90041685565b60066020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b600054600160f01b900460ff16612054576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b1916905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b1580156120c157600080fd5b505afa1580156120d5573d6000803e3d6000fd5b505050506040513d60208110156120eb57600080fd5b50516001600160a01b0316331461210157600080fd5b60ff82161580612124575060048260ff16101580156121245750600a8260ff1611155b801561214e575060ff8116158061214e575060048160ff161015801561214e5750600a8160ff1611155b61215757600080fd5b60008054610ff0600484901b16840160ff908116600160e81b9081027fffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841617909355919004167f973d8d92bb299f4af6ce49b52a8adb85ae46b9f214c4c4fc06ac77401237b1336010826040805160ff9390920683168252600f600486901c16602083015286831682820152918516606082015290519081900360800190a150506000805460ff60f01b1916600160f01b17905550565b600080548190600160f01b900460ff16612256576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b1916905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b1580156122c357600080fd5b505afa1580156122d7573d6000803e3d6000fd5b505050506040513d60208110156122ed57600080fd5b50516001600160a01b0316331461230357600080fd5b6003546001600160801b039081169085161161231f578361232c565b6003546001600160801b03165b6003549092506001600160801b03600160801b9091048116908416116123525782612366565b600354600160801b90046001600160801b03165b90506001600160801b038216156123e7576003546001600160801b038381169116141561239557600019909101905b600380546001600160801b031981166001600160801b039182168590038216179091556123e7907f00000000000000000000000000000000000000000000000000000000000000009087908516613b86565b6001600160801b0381161561246d576003546001600160801b03828116600160801b90920416141561241857600019015b600380546001600160801b03600160801b80830482168590038216029181169190911790915561246d907f00000000000000000000000000000000000000000000000000000000000000009087908416613b86565b604080516001600160801b0380851682528316602082015281516001600160a01b0388169233927f596b573906218d3411850b26a6b437d6c4522fdb43d2d2386263f86d50b8b151929081900390910190a36000805460ff60f01b1916600160f01b1790559094909350915050565b6060806124e7612bf0565b61255e6124f2612c27565b858580806020026020016040519081016040528093929190818152602001838360200280828437600092018290525054600454600896959450600160a01b820460020b935061ffff600160b81b8304811693506001600160801b0390911691600160c81b900416614247565b915091509250929050565b600080548190600160f01b900460ff166125b0576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b1916815560408051608081018252338152600288810b602083015287900b918101919091528190819061260990606081016125fc6001600160801b038a16613f58565b600003600f0b9052613f69565b925092509250816000039450806000039350600085118061262a5750600084115b15612669576003830180546001600160801b038082168089018216600160801b93849004831689019092169092029091176001600160801b0319161790555b604080516001600160801b0388168152602081018790528082018690529051600289810b92908b900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a450506000805460ff60f01b1916600160f01b179055509094909350915050565b60008060006126ed612bf0565b6126f785856143a1565b600285810b810b60009081526005602052604080822087840b90930b825281206003830154600681900b9367010000000000000082046001600160a01b0316928492600160d81b810463ffffffff169284929091600160f81b900460ff168061275f57600080fd5b6003820154600681900b985067010000000000000081046001600160a01b03169650600160d81b810463ffffffff169450600160f81b900460ff16806127a457600080fd5b50506040805160e0810182526000546001600160a01b0381168252600160a01b8104600290810b810b810b6020840181905261ffff600160b81b8404811695850195909552600160c81b830485166060850152600160d81b8304909416608084015260ff600160e81b8304811660a0850152600160f01b909204909116151560c08301529093508e810b91900b1215905061284d575093909403965090039350900390506128d0565b8a60020b816020015160020b12156128c1576000612869612c27565b602083015160408401516004546060860151939450600093849361289f936008938893879392916001600160801b031690613389565b9a9003989098039b5050949096039290920396509091030392506128d0915050565b50949093039650039350900390505b9250925092565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60015481565b60056020526000908152604090208054600182015460028301546003909301546001600160801b03831693600160801b909304600f0b9290600681900b9067010000000000000081046001600160a01b031690600160d81b810463ffffffff1690600160f81b900460ff1688565b6000546001600160a01b031615612a1e576040805162461bcd60e51b8152602060048201526002602482015261414960f01b604482015290519081900360640190fd5b6000612a29826136a5565b9050600080612a41612a39612c27565b60089061446a565b6040805160e0810182526001600160a01b038816808252600288810b6020808501829052600085870181905261ffff898116606088018190529089166080880181905260a08801839052600160c0909801979097528154600160f01b73ffffffffffffffffffffffffffffffffffffffff19909116871762ffffff60a01b1916600160a01b62ffffff9787900b9790971696909602959095177fffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffff16600160c81b9091021761ffff60d81b1916600160d81b909602959095177fff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1692909217909355835191825281019190915281519395509193507f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c9592918290030190a150505050565b60008082600281900b620d89e71981612b9957fe5b05029050600083600281900b620d89e881612bb057fe5b0502905060008460020b83830360020b81612bc757fe5b0560010190508062ffffff166001600160801b03801681612be457fe5b0493505050505b919050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612c2557600080fd5b565b4290565b60008060008460020b8660020b81612c3f57fe5b05905060008660020b128015612c6657508460020b8660020b81612c5f57fe5b0760020b15155b15612c7057600019015b8315612ce557600080612c82836144b6565b600182810b810b600090815260208d9052604090205460ff83169190911b80016000190190811680151597509294509092509085612cc757888360ff16860302612cda565b88612cd1826144c8565b840360ff168603025b965050505050612d63565b600080612cf4836001016144b6565b91509150600060018260ff166001901b031990506000818b60008660010b60010b8152602001908152602001600020541690508060001415955085612d4657888360ff0360ff16866001010102612d5c565b8883612d5183614568565b0360ff168660010101025b9650505050505b5094509492505050565b60008060008360020b12612d84578260020b612d8c565b8260020b6000035b9050620d89e8811115612dca576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b600060018216612dde57600160801b612df0565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615612e24576ffff97272373d413259a46990580e213a0260801c5b6004821615612e43576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615612e62576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615612e81576fffcb9843d60f6159c9db58835c9266440260801c5b6020821615612ea0576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615612ebf576fff2ea16466c96a3843ec78b326b528610260801c5b6080821615612ede576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615612efe576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615612f1e576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615612f3e576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615612f5e576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615612f7e576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615612f9e576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615612fbe576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615612fde576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615612fff576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b6202000082161561301f576e5d6af8dedb81196699c329225ee6040260801c5b6204000082161561303e576d2216e584f5fa1ea926041bedfe980260801c5b6208000082161561305b576b048a170391f7dc42444e8fa20260801c5b60008460020b131561307657806000198161307257fe5b0490505b64010000000081061561308a57600161308d565b60005b60ff16602082901c0192505050919050565b60008080806001600160a01b03808916908a1610158187128015906131245760006130d88989620f42400362ffffff16620f42406132d9565b9050826130f1576130ec8c8c8c6001614652565b6130fe565b6130fe8b8d8c60016146cd565b955085811061310f578a965061311e565b61311b8c8b838661478a565b96505b5061316e565b8161313b576131368b8b8b60006146cd565b613148565b6131488a8c8b6000614652565b935083886000031061315c5789955061316e565b61316b8b8a8a600003856147d6565b95505b6001600160a01b038a81169087161482156131d15780801561318d5750815b6131a35761319e878d8c60016146cd565b6131a5565b855b95508080156131b2575081155b6131c8576131c3878d8c6000614652565b6131ca565b845b945061321b565b8080156131db5750815b6131f1576131ec8c888c6001614652565b6131f3565b855b9550808015613200575081155b613216576132118c888c60006146cd565b613218565b845b94505b8115801561322b57508860000385115b15613237578860000394505b81801561325657508a6001600160a01b0316876001600160a01b031614155b15613265578589039350613282565b61327f868962ffffff168a620f42400362ffffff166141a9565b93505b50505095509550955095915050565b6000600160ff1b82106132a357600080fd5b5090565b808203828113156000831215146132bd57600080fd5b92915050565b818101828112156000831215146132bd57600080fd5b600080806000198587098686029250828110908390030390508061330f576000841161330457600080fd5b508290049050613382565b80841161331b57600080fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150505b9392505050565b60008063ffffffff8716613430576000898661ffff1661ffff81106133aa57fe5b60408051608081018252919092015463ffffffff8082168084526401000000008304600690810b810b900b6020850152600160581b83046001600160a01b031694840194909452600160f81b90910460ff16151560608301529092508a161461341c57613419818a8988614822565b90505b806020015181604001519250925050613510565b8688036000806134458c8c858c8c8c8c6148d2565b91509150816000015163ffffffff168363ffffffff161415613477578160200151826040015194509450505050613510565b805163ffffffff8481169116141561349f578060200151816040015194509450505050613510565b8151815160208085015190840151918390039286039163ffffffff80841692908516910360060b816134cd57fe5b05028460200151018263ffffffff168263ffffffff1686604001518660400151036001600160a01b031602816134ff57fe5b048560400151019650965050505050505b97509795505050505050565b600295860b860b60009081526020979097526040909620600181018054909503909455938301805490920390915560038201805463ffffffff600160d81b6001600160a01b036701000000000000008085048216909603169094027fffffffffff0000000000000000000000000000000000000000ffffffffffffff90921691909117600681810b90960390950b66ffffffffffffff1666ffffffffffffff199095169490941782810485169095039093160263ffffffff60d81b1990931692909217905554600160801b9004600f0b90565b60008082600f0b121561365457826001600160801b03168260000384039150816001600160801b03161061364f576040805162461bcd60e51b81526020600482015260026024820152614c5360f01b604482015290519081900360640190fd5b6132bd565b826001600160801b03168284019150816001600160801b031610156132bd576040805162461bcd60e51b81526020600482015260026024820152614c4160f01b604482015290519081900360640190fd5b60006401000276a36001600160a01b038316108015906136e1575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038316105b613716576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b77ffffffffffffffffffffffffffffffffffffffff00000000602083901b166001600160801b03811160071b81811c67ffffffffffffffff811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c979088119617909417909217179091171717608081106137b757607f810383901c91506137c1565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d607f198f0160401b60c09190911c678000000000000000161760c19b909b1c674000000000000000169a909a1760c29990991c672000000000000000169890981760c39790971c671000000000000000169690961760c49590951c670800000000000000169490941760c59390931c670400000000000000169290921760c69190911c670200000000000000161760c79190911c670100000000000000161760c89190911c6680000000000000161760c99190911c6640000000000000161760ca9190911c6620000000000000161760cb9190911c6610000000000000161760cc9190911c6608000000000000161760cd9190911c66040000000000001617693627a301d71055774c8581026f028f6481ab7f045a5af012a19d003aa9198101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b146139c257886001600160a01b03166139a682612d6d565b6001600160a01b031611156139bb57816139bd565b805b6139c4565b815b9998505050505050505050565b6000806000898961ffff1661ffff81106139e757fe5b60408051608081018252919092015463ffffffff8082168084526401000000008304600690810b810b900b6020850152600160581b83046001600160a01b031694840194909452600160f81b90910460ff161515606083015290925089161415613a575788859250925050613510565b8461ffff168461ffff16118015613a7857506001850361ffff168961ffff16145b15613a8557839150613a89565b8491505b8161ffff168960010161ffff1681613a9d57fe5b069250613aac81898989614822565b8a8461ffff1661ffff8110613abd57fe5b825191018054602084015160408501516060909501511515600160f81b027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001600160a01b03909616600160581b027fff0000000000000000000000000000000000000000ffffffffffffffffffffff60069390930b66ffffffffffffff16640100000000026affffffffffffff000000001963ffffffff90971663ffffffff199095169490941795909516929092171692909217929092161790555097509795505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b60208310613c025780518252601f199092019160209182019101613be3565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613c64576040519150601f19603f3d011682016040523d82523d6000602084013e613c69565b606091505b5091509150818015613c97575080511580613c975750808060200190516020811015613c9457600080fd5b50515b613ccd576040805162461bcd60e51b81526020600482015260026024820152612a2360f11b604482015290519081900360640190fd5b5050505050565b604080513060248083019190915282518083039091018152604490910182526020810180516001600160e01b03166370a0823160e01b17815291518151600093849384936001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693919290918291908083835b60208310613d6d5780518252601f199092019160209182019101613d4e565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613dcd576040519150601f19603f3d011682016040523d82523d6000602084013e613dd2565b606091505b5091509150818015613de657506020815110155b613def57600080fd5b808060200190516020811015613e0457600080fd5b50519250505090565b808201828110156132bd57600080fd5b604080513060248083019190915282518083039091018152604490910182526020810180516001600160e01b03166370a0823160e01b17815291518151600093849384936001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016939192909182919080838360208310613d6d5780518252601f199092019160209182019101613d4e565b6000808361ffff1611613ef3576040805162461bcd60e51b81526020600482015260016024820152604960f81b604482015290519081900360640190fd5b8261ffff168261ffff1611613f09575081613382565b825b8261ffff168161ffff161015613f4f576001858261ffff1661ffff8110613f2e57fe5b01805463ffffffff191663ffffffff92909216919091179055600101613f0b565b50909392505050565b80600f81900b8114612beb57600080fd5b6000806000613f76612bf0565b613f88846020015185604001516143a1565b6040805160e0810182526000546001600160a01b0381168252600160a01b8104600290810b810b900b602080840182905261ffff600160b81b8404811685870152600160c81b84048116606080870191909152600160d81b8504909116608086015260ff600160e81b8504811660a0870152600160f01b909404909316151560c08501528851908901519489015192890151939461402c9491939092909190614acf565b93508460600151600f0b6000146141a157846020015160020b816020015160020b12156140815761407a6140638660200151612d6d565b6140708760400151612d6d565b8760600151614c84565b92506141a1565b846040015160020b816020015160020b12156141775760045460408201516001600160801b03909116906140d3906140b7612c27565b60208501516060860151608087015160089493929187916139d1565b6000805461ffff60c81b1916600160c81b61ffff938416021761ffff60b81b1916600160b81b939092169290920217905581516040870151614123919061411990612d6d565b8860600151614c84565b93506141416141358760200151612d6d565b83516060890151614cc8565b92506141518187606001516135ef565b600480546001600160801b0319166001600160801b0392909216919091179055506141a1565b61419e6141878660200151612d6d565b6141948760400151612d6d565b8760600151614cc8565b91505b509193909250565b60006141b68484846132d9565b9050600082806141c257fe5b84860911156133825760001981106141d957600080fd5b6001019392505050565b6040805160609490941b6bffffffffffffffffffffffff1916602080860191909152600293840b60e890811b60348701529290930b90911b60378401528051808403601a018152603a90930181528251928201929092206000908152929052902090565b60608060008361ffff1611614287576040805162461bcd60e51b81526020600482015260016024820152604960f81b604482015290519081900360640190fd5b865167ffffffffffffffff8111801561429f57600080fd5b506040519080825280602002602001820160405280156142c9578160200160208202803683370190505b509150865167ffffffffffffffff811180156142e457600080fd5b5060405190808252806020026020018201604052801561430e578160200160208202803683370190505b50905060005b87518110156143945761433f8a8a8a848151811061432e57fe5b60200260200101518a8a8a8a613389565b84838151811061434b57fe5b6020026020010184848151811061435e57fe5b60200260200101826001600160a01b03166001600160a01b03168152508260060b60060b81525050508080600101915050614314565b5097509795505050505050565b8060020b8260020b126143e1576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e719600283900b1215614424576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b620d89e8600282900b1315614466576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b5050565b6040805160808101825263ffffffff9283168082526000602083018190529282019290925260016060909101819052835463ffffffff1916909117909116600160f81b17909155908190565b60020b600881901d9161010090910790565b60008082116144d657600080fd5b600160801b82106144e957608091821c91015b68010000000000000000821061450157604091821c91015b640100000000821061451557602091821c91015b62010000821061452757601091821c91015b610100821061453857600891821c91015b6010821061454857600491821c91015b6004821061455857600291821c91015b60028210612beb57600101919050565b600080821161457657600080fd5b5060ff6001600160801b0382161561459157607f1901614599565b608082901c91505b67ffffffffffffffff8216156145b257603f19016145ba565b604082901c91505b63ffffffff8216156145cf57601f19016145d7565b602082901c91505b61ffff8216156145ea57600f19016145f2565b601082901c91505b60ff821615614604576007190161460c565b600882901c91505b600f82161561461e5760031901614626565b600482901c91505b60038216156146385760011901614640565b600282901c91505b6001821615612beb5760001901919050565b6000836001600160a01b0316856001600160a01b03161115614672579293925b8161469f5761469a836001600160801b03168686036001600160a01b0316600160601b6132d9565b6146c2565b6146c2836001600160801b03168686036001600160a01b0316600160601b6141a9565b90505b949350505050565b6000836001600160a01b0316856001600160a01b031611156146ed579293925b7bffffffffffffffffffffffffffffffff000000000000000000000000606084901b166001600160a01b03868603811690871661472957600080fd5b8361475957866001600160a01b031661474c8383896001600160a01b03166132d9565b8161475357fe5b0461477f565b61477f6147708383896001600160a01b03166141a9565b886001600160a01b0316614cf7565b979650505050505050565b600080856001600160a01b0316116147a157600080fd5b6000846001600160801b0316116147b757600080fd5b816147c95761469a8585856001614d02565b6146c28585856001614de3565b600080856001600160a01b0316116147ed57600080fd5b6000846001600160801b03161161480357600080fd5b816148155761469a8585856000614de3565b6146c28585856000614d02565b61482a61564a565b600085600001518503905060405180608001604052808663ffffffff1681526020018263ffffffff168660020b0288602001510160060b81526020016000856001600160801b03161161487e576001614880565b845b6001600160801b031673ffffffff00000000000000000000000000000000608085901b16816148ab57fe5b048860400151016001600160a01b0316815260200160011515815250915050949350505050565b6148da61564a565b6148e261564a565b888561ffff1661ffff81106148f357fe5b60408051608081018252919092015463ffffffff81168083526401000000008204600690810b810b900b6020840152600160581b82046001600160a01b031693830193909352600160f81b900460ff1615156060820152925061495890899089614ed8565b15614990578663ffffffff16826000015163ffffffff16141561497a57613510565b8161498783898988614822565b91509150613510565b888361ffff168660010161ffff16816149a557fe5b0661ffff1661ffff81106149b557fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b60208401526001600160a01b03600160581b8204169183019190915260ff600160f81b90910416151560608201819052909250614a6c57604080516080810182528a5463ffffffff811682526401000000008104600690810b810b900b6020830152600160581b81046001600160a01b031692820192909252600160f81b90910460ff161515606082015291505b614a7b88836000015189614ed8565b614ab2576040805162461bcd60e51b815260206004820152600360248201526213d31160ea1b604482015290519081900360640190fd5b614abf8989898887614f9b565b9150915097509795505050505050565b6000614ade60078787876141e3565b60015460025491925090600080600f87900b15614c24576000614aff612c27565b6000805460045492935090918291614b499160089186918591600160a01b810460020b9161ffff600160b81b83048116926001600160801b0390921691600160c81b900416613389565b9092509050614b8360058d8b8d8b8b87898b60007f000000000000000000000000000000000000000000000000000000000000000061513b565b9450614bba60058c8b8d8b8b87898b60017f000000000000000000000000000000000000000000000000000000000000000061513b565b93508415614bee57614bee60068d7f0000000000000000000000000000000000000000000000000000000000000000615325565b8315614c2057614c2060068c7f0000000000000000000000000000000000000000000000000000000000000000615325565b5050505b600080614c3660058c8c8b8a8a61538b565b9092509050614c47878a8484615437565b600089600f0b1215614c75578315614c6457614c6460058c6155cc565b8215614c7557614c7560058b6155cc565b50505050505095945050505050565b60008082600f0b12614caa57614ca5614ca085858560016146cd565b613291565b6146c5565b614cbd614ca085858560000360006146cd565b600003949350505050565b60008082600f0b12614ce457614ca5614ca08585856001614652565b614cbd614ca08585856000036000614652565b808204910615150190565b60008115614d755760006001600160a01b03841115614d3857614d3384600160601b876001600160801b03166132d9565b614d50565b6001600160801b038516606085901b81614d4e57fe5b045b9050614d6d614d686001600160a01b03881683613e0d565b6155f8565b9150506146c5565b60006001600160a01b03841115614da357614d9e84600160601b876001600160801b03166141a9565b614dba565b614dba606085901b6001600160801b038716614cf7565b905080866001600160a01b031611614dd157600080fd5b6001600160a01b0386160390506146c5565b600082614df15750836146c5565b7bffffffffffffffffffffffffffffffff000000000000000000000000606085901b168215614e91576001600160a01b03861684810290858281614e3157fe5b041415614e6257818101828110614e6057614e5683896001600160a01b0316836141a9565b93505050506146c5565b505b614e8882614e83878a6001600160a01b03168681614e7c57fe5b0490613e0d565b614cf7565b925050506146c5565b6001600160a01b03861684810290858281614ea857fe5b04148015614eb557508082115b614ebe57600080fd5b808203614e56614d68846001600160a01b038b16846141a9565b60008363ffffffff168363ffffffff1611158015614f0257508363ffffffff168263ffffffff1611155b15614f1e578163ffffffff168363ffffffff1611159050613382565b60008463ffffffff168463ffffffff1611614f46578363ffffffff1664010000000001614f4e565b8363ffffffff165b64ffffffffff16905060008563ffffffff168463ffffffff1611614f7f578363ffffffff1664010000000001614f87565b8363ffffffff165b64ffffffffff169091111595945050505050565b614fa361564a565b614fab61564a565b60008361ffff168560010161ffff1681614fc157fe5b0661ffff169050600060018561ffff16830103905060005b506002818301048961ffff87168281614fee57fe5b0661ffff8110614ffa57fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b60208401526001600160a01b03600160581b8204169183019190915260ff600160f81b9091041615156060820181905290955061506557806001019250614fd9565b898661ffff16826001018161507657fe5b0661ffff811061508257fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b60208401526001600160a01b03600160581b8204169183019190915260ff600160f81b909104161515606082015285519094506000906150ed908b908b614ed8565b905080801561510657506151068a8a8760000151614ed8565b15615111575061512e565b8061512157600182039250615128565b8160010193505b50614fd9565b5050509550959350505050565b60028a810b900b600090815260208c90526040812080546001600160801b031682615166828d6135ef565b9050846001600160801b0316816001600160801b031611156151b4576040805162461bcd60e51b81526020600482015260026024820152614c4f60f01b604482015290519081900360640190fd5b6001600160801b03828116159082161581141594501561528a578c60020b8e60020b1361525a57600183018b9055600283018a90556003830180547fffffffffff0000000000000000000000000000000000000000ffffffffffffff166701000000000000006001600160a01b038c16021766ffffffffffffff191666ffffffffffffff60068b900b161763ffffffff60d81b1916600160d81b63ffffffff8a16021790555b6003830180547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160f81b1790555b82546001600160801b0319166001600160801b038216178355856152d35782546152ce906152c990600160801b9004600f90810b810b908f900b6132c3565b613f58565b6152f4565b82546152f4906152c990600160801b9004600f90810b810b908f900b6132a7565b8354600f9190910b6001600160801b03908116600160801b0291161790925550909c9b505050505050505050505050565b8060020b8260020b8161533457fe5b0760020b1561534257600080fd5b60008061535d8360020b8560020b8161535757fe5b056144b6565b600191820b820b60009081526020979097526040909620805460ff9097169190911b90951890945550505050565b600285810b80820b60009081526020899052604080822088850b850b83529082209193849391929184918291908a900b126153d1575050600182015460028301546153e4565b8360010154880391508360020154870390505b6000808b60020b8b60020b121561540657505060018301546002840154615419565b84600101548a0391508460020154890390505b92909803979097039b96909503949094039850939650505050505050565b6040805160a08101825285546001600160801b0390811682526001870154602083015260028701549282019290925260038601548083166060830152600160801b900490911660808201526000600f85900b6154d65781516001600160801b03166154ce576040805162461bcd60e51b815260206004820152600260248201526104e560f41b604482015290519081900360640190fd5b5080516154e5565b81516154e290866135ef565b90505b60006155098360200151860384600001516001600160801b0316600160801b6132d9565b9050600061552f8460400151860385600001516001600160801b0316600160801b6132d9565b905086600f0b6000146155565787546001600160801b0319166001600160801b0384161788555b60018801869055600288018590556001600160801b03821615158061558457506000816001600160801b0316115b156155c2576003880180546001600160801b031981166001600160801b039182168501821617808216600160801b9182900483168501909216021790555b5050505050505050565b600290810b810b6000908152602092909252604082208281556001810183905590810182905560030155565b806001600160a01b0381168114612beb57600080fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040805160808101825260008082526020820181905291810182905260608101919091529056fea164736f6c6343000706000a" + } + ], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": "0xcE2c49b75fF7a0C5f45EffDB2b6A776e60A0deCb", + "cumulativeGasUsed": "0x52362e", + "gasUsed": "0x52362e", + "contractAddress": null, + "logs": [ + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb" + ], + "data": "0x0000000000000000000000000000000000000002863c1f5cdae42f9540000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x1", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x0000000000000000000000001238536071e1c677a632429e3655c799b22cda52" + ], + "data": "0x00000000000000000000000000000000000000026c62ad77dc602dae00000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x2", + "removed": false + }, + { + "address": "0x0227628f3F023bb0B980b67D528571c95c6DaC1c", + "topics": [ + "0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118", + "0x000000000000000000000000153a1635ab52e15969fd65a72594a9875d87fcfd", + "0x000000000000000000000000fff9976782d46cc05630d1f6ebab18b2324d6b14", + "0x0000000000000000000000000000000000000000000000000000000000002710" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000c80000000000000000000000009cbac168dda0f817d1afa06bca721902ac8c364e", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x3", + "removed": false + }, + { + "address": "0x9CBAc168ddA0f817D1AFA06BCa721902AC8C364e", + "topics": [ + "0x98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95" + ], + "data": "0x0000000000000000000000000000000000000000000109443ac9bc7d1f96691cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca04d", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x4", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x0000000000000000000000009cbac168dda0f817d1afa06bca721902ac8c364e" + ], + "data": "0x00000000000000000000000000000000000000026c62ad77dc602dadffffae12", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x5", + "removed": false + }, + { + "address": "0x9CBAc168ddA0f817D1AFA06BCa721902AC8C364e", + "topics": [ + "0x7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde", + "0x0000000000000000000000001238536071e1c677a632429e3655c799b22cda52", + "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca310", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x0000000000000000000000001238536071e1c677a632429e3655c799b22cda520000000000000000000000000000000000000000000299fb7dca14626ce38c7600000000000000000000000000000000000000026c62ad77dc602dadffffae120000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x6", + "removed": false + }, + { + "address": "0x1238536071E1c677A632429e3655c799b22cDA52", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x00000000000000000000000000000000000000000000000000000000000034be" + ], + "data": "0x", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x7", + "removed": false + }, + { + "address": "0x1238536071E1c677A632429e3655c799b22cDA52", + "topics": [ + "0x3067048beee31b25b2f1681f88dac838c8bba36af25bfb2b7cf7473a5847e35f", + "0x00000000000000000000000000000000000000000000000000000000000034be" + ], + "data": "0x0000000000000000000000000000000000000000000299fb7dca14626ce38c7600000000000000000000000000000000000000026c62ad77dc602dadffffae120000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x8", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb" + ], + "data": "0x000000000000000000000000000000000000000019d971e4fe8401e740000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x9", + "removed": false + }, + { + "address": "0xcE2c49b75fF7a0C5f45EffDB2b6A776e60A0deCb", + "topics": [ + "0x0d1d2eaa1e5bac93f8aaaed98c5de5ec54cdeb3867b3238f239475dfb0dd337d", + "0x000000000000000000000000153a1635ab52e15969fd65a72594a9875d87fcfd", + "0x00000000000000000000000000000000000000000000000000000000000034be", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xa", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef" + ], + "data": "0x000000000000000000000000000000000000000019d971e4fe8401e740000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xb", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef" + ], + "data": "0x000000000000000000000000000000000000000019d971e4fe8401e740000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xc", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef", + "0x0000000000000000000000000000000000000000000000000000000000000001" + ], + "data": "0x000000000000000000000000000000000000000006765c793fa10079d0000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xd", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef", + "0x0000000000000000000000000000000000000000000000000000000000000002" + ], + "data": "0x000000000000000000000000000000000000000006765c793fa10079d0000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xe", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef", + "0x0000000000000000000000000000000000000000000000000000000000000003" + ], + "data": "0x000000000000000000000000000000000000000006765c793fa10079d0000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xf", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" + ], + "data": "0x000000000000000000000000000000000000000006765c793fa10079d0000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x55ffec", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x10", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x04000800011000000100100000000080000020000000000000000000000000000100000000000000200400000000000080000800020080000400000000240000000001000000000000000009010000000001000000040040000200000000000020000000222000000000000100000800080000000800000000000010000000000020000018000000010800010008000000000010000080000004000000400000020000000000000000010100000100000000000040800000000000002400480000000412100040200002080001020000202000002004000000000000800060000010400000000000000000000000000000200010008000000000000200000800", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e02" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1712342850, + "chain": 11155111, + "commit": "94af5ff" +} \ No newline at end of file diff --git a/broadcast/DeployTokenV2.s.sol/11155111/run-1712343176.json b/broadcast/DeployTokenV2.s.sol/11155111/run-1712343176.json new file mode 100644 index 0000000..635943d --- /dev/null +++ b/broadcast/DeployTokenV2.s.sol/11155111/run-1712343176.json @@ -0,0 +1,318 @@ +{ + "transactions": [ + { + "hash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xcE2c49b75fF7a0C5f45EffDB2b6A776e60A0deCb", + "function": "deployWithAirdrop(string,string,address[])", + "arguments": [ + "\"\"", + "\"\"", + "[0x0000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000003]" + ], + "transaction": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "gas": "0x718656", + "value": "0x0", + "data": "0x34967f580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + "nonce": "0x1526", + "accessList": [] + }, + "additionalContracts": [ + { + "transactionType": "CREATE2", + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "initCode": "0x3d602d80600a3d3981f3363d3d373d3d3d363d73d74d14ebe305c93d023c966640788f05593f0fde5af43d82803e903d91602b57fd5bf3" + }, + { + "transactionType": "CREATE2", + "address": "0x9CBAc168ddA0f817D1AFA06BCa721902AC8C364e", + "initCode": "0x6101606040523480156200001257600080fd5b503060601b60805260408051630890357360e41b81529051600091339163890357309160048082019260a092909190829003018186803b1580156200005657600080fd5b505afa1580156200006b573d6000803e3d6000fd5b505050506040513d60a08110156200008257600080fd5b508051602080830151604084015160608086015160809096015160e896871b6001600160e81b0319166101005291811b6001600160601b031990811660e05292811b831660c0529390931b1660a052600282810b900b90921b610120529150620000f79082906200010f811b62002b8417901c565b60801b6001600160801b03191661014052506200017d565b60008082600281900b620d89e719816200012557fe5b05029050600083600281900b620d89e8816200013d57fe5b0502905060008460020b83830360020b816200015557fe5b0560010190508062ffffff166001600160801b038016816200017357fe5b0495945050505050565b60805160601c60a05160601c60c05160601c60e05160601c6101005160e81c6101205160e81c6101405160801c61567e6200024a60003980611fee5280614b5f5280614b96525080610c0052806128fd5280614bca5280614bfc525080610cef52806119cb5280611a0252806129455250806111c75280611a855280611ef4528061244452806129215280613e6b5250806108d252806112f55280611a545280611e8e52806123be5280613d2252508061207b528061227d52806128d9525080612bfb525061567e6000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806370cf754a116100ee578063c45a015511610097578063ddca3f4311610071578063ddca3f4314610800578063f305839914610820578063f30dba9314610828578063f637731d146108aa576101ae565b8063c45a0155146107d1578063d0c93a7c146107d9578063d21220a7146107f8576101ae565b8063883bdbfd116100c8578063883bdbfd14610633578063a34123a71461073c578063a38807f214610776576101ae565b806370cf754a146105c65780638206a4d1146105ce57806385b66729146105f6576101ae565b80633850c7bd1161015b578063490e6cbc11610135578063490e6cbc146104705780634f1eb3d8146104fc578063514ea4bf1461054d5780635339c296146105a6576101ae565b80633850c7bd1461035b5780633c8a7d8d146103b45780634614131914610456576101ae565b80631ad8b03b1161018c5780631ad8b03b146102aa578063252c09d7146102e157806332148f6714610338576101ae565b80630dfe1681146101b3578063128acb08146101d75780631a68650214610286575b600080fd5b6101bb6108d0565b604080516001600160a01b039092168252519081900360200190f35b61026d600480360360a08110156101ed57600080fd5b6001600160a01b0382358116926020810135151592604082013592606083013516919081019060a08101608082013564010000000081111561022e57600080fd5b82018360208201111561024057600080fd5b8035906020019184600183028401116401000000008311171561026257600080fd5b5090925090506108f4565b6040805192835260208301919091528051918290030190f35b61028e6114ad565b604080516001600160801b039092168252519081900360200190f35b6102b26114bc565b60405180836001600160801b03168152602001826001600160801b031681526020019250505060405180910390f35b6102fe600480360360208110156102f757600080fd5b50356114d6565b6040805163ffffffff909516855260069390930b60208501526001600160a01b039091168383015215156060830152519081900360800190f35b6103596004803603602081101561034e57600080fd5b503561ffff1661151c565b005b610363611616565b604080516001600160a01b03909816885260029690960b602088015261ffff9485168787015292841660608701529216608085015260ff90911660a0840152151560c0830152519081900360e00190f35b61026d600480360360a08110156103ca57600080fd5b6001600160a01b03823516916020810135600290810b92604083013590910b916001600160801b036060820135169181019060a08101608082013564010000000081111561041757600080fd5b82018360208201111561042957600080fd5b8035906020019184600183028401116401000000008311171561044b57600080fd5b509092509050611666565b61045e611922565b60408051918252519081900360200190f35b6103596004803603608081101561048657600080fd5b6001600160a01b0382351691602081013591604082013591908101906080810160608201356401000000008111156104bd57600080fd5b8201836020820111156104cf57600080fd5b803590602001918460018302840111640100000000831117156104f157600080fd5b509092509050611928565b6102b2600480360360a081101561051257600080fd5b506001600160a01b03813516906020810135600290810b91604081013590910b906001600160801b0360608201358116916080013516611d83565b61056a6004803603602081101561056357600080fd5b5035611f9d565b604080516001600160801b0396871681526020810195909552848101939093529084166060840152909216608082015290519081900360a00190f35b61045e600480360360208110156105bc57600080fd5b503560010b611fda565b61028e611fec565b610359600480360360408110156105e457600080fd5b5060ff81358116916020013516612010565b6102b26004803603606081101561060c57600080fd5b506001600160a01b03813516906001600160801b036020820135811691604001351661220f565b6106a36004803603602081101561064957600080fd5b81019060208101813564010000000081111561066457600080fd5b82018360208201111561067657600080fd5b8035906020019184602083028401116401000000008311171561069857600080fd5b5090925090506124dc565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156106e75781810151838201526020016106cf565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561072657818101518382015260200161070e565b5050505090500194505050505060405180910390f35b61026d6004803603606081101561075257600080fd5b508035600290810b91602081013590910b90604001356001600160801b0316612569565b6107a06004803603604081101561078c57600080fd5b508035600290810b9160200135900b6126e0565b6040805160069490940b84526001600160a01b03909216602084015263ffffffff1682820152519081900360600190f35b6101bb6128d7565b6107e16128fb565b6040805160029290920b8252519081900360200190f35b6101bb61291f565b610808612943565b6040805162ffffff9092168252519081900360200190f35b61045e612967565b6108486004803603602081101561083e57600080fd5b503560020b61296d565b604080516001600160801b039099168952600f9790970b602089015287870195909552606087019390935260069190910b60808601526001600160a01b031660a085015263ffffffff1660c0840152151560e083015251908190036101000190f35b610359600480360360208110156108c057600080fd5b50356001600160a01b03166129db565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806108ff612bf0565b85610936576040805162461bcd60e51b8152602060048201526002602482015261415360f01b604482015290519081900360640190fd5b6040805160e0810182526000546001600160a01b0381168252600160a01b8104600290810b810b900b602083015261ffff600160b81b8204811693830193909352600160c81b810483166060830152600160d81b8104909216608082015260ff600160e81b8304811660a0830152600160f01b909204909116151560c082018190526109ef576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b87610a3a5780600001516001600160a01b0316866001600160a01b0316118015610a35575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038716105b610a6c565b80600001516001600160a01b0316866001600160a01b0316108015610a6c57506401000276a36001600160a01b038716115b610aa3576040805162461bcd60e51b815260206004820152600360248201526214d41360ea1b604482015290519081900360640190fd5b6000805460ff60f01b191681556040805160c08101909152808a610ad25760048460a0015160ff16901c610ae5565b60108460a0015160ff1681610ae357fe5b065b60ff1681526004546001600160801b03166020820152604001610b06612c27565b63ffffffff168152602001600060060b815260200160006001600160a01b031681526020016000151581525090506000808913905060006040518060e001604052808b81526020016000815260200185600001516001600160a01b03168152602001856020015160020b81526020018c610b8257600254610b86565b6001545b815260200160006001600160801b0316815260200184602001516001600160801b031681525090505b805115801590610bd55750886001600160a01b031681604001516001600160a01b031614155b15610f9f57610be261560e565b60408201516001600160a01b031681526060820151610c25906006907f00000000000000000000000000000000000000000000000000000000000000008f612c2b565b15156040830152600290810b810b60208301819052620d89e719910b1215610c5657620d89e7196020820152610c75565b6020810151620d89e860029190910b1315610c7557620d89e860208201525b610c828160200151612d6d565b6001600160a01b031660608201526040820151610d13908d610cbc578b6001600160a01b031683606001516001600160a01b031611610cd6565b8b6001600160a01b031683606001516001600160a01b0316105b610ce4578260600151610ce6565b8b5b60c085015185517f000000000000000000000000000000000000000000000000000000000000000061309f565b60c085015260a084015260808301526001600160a01b031660408301528215610d7557610d498160c00151826080015101613291565b825103825260a0810151610d6b90610d6090613291565b6020840151906132a7565b6020830152610db0565b610d828160a00151613291565b825101825260c08101516080820151610daa91610d9f9101613291565b6020840151906132c3565b60208301525b835160ff1615610df6576000846000015160ff168260c0015181610dd057fe5b60c0840180519290910491829003905260a0840180519091016001600160801b03169052505b60c08201516001600160801b031615610e3557610e298160c00151600160801b8460c001516001600160801b03166132d9565b60808301805190910190525b80606001516001600160a01b031682604001516001600160a01b03161415610f5e57806040015115610f35578360a00151610ebf57610e9d846040015160008760200151886040015188602001518a606001516008613389909695949392919063ffffffff16565b6001600160a01b03166080860152600690810b900b6060850152600160a08501525b6000610f0b82602001518e610ed657600154610edc565b84608001515b8f610eeb578560800151610eef565b6002545b608089015160608a015160408b0151600595949392919061351c565b90508c15610f17576000035b610f258360c00151826135ef565b6001600160801b031660c0840152505b8b610f44578060200151610f4d565b60018160200151035b600290810b900b6060830152610f99565b80600001516001600160a01b031682604001516001600160a01b031614610f9957610f8c82604001516136a5565b600290810b900b60608301525b50610baf565b836020015160020b816060015160020b1461107a57600080610fed86604001518660400151886020015188602001518a606001518b6080015160086139d1909695949392919063ffffffff16565b604085015160608601516000805461ffff60c81b1916600160c81b61ffff958616021761ffff60b81b1916600160b81b95909416949094029290921762ffffff60a01b1916600160a01b62ffffff60029490940b93909316929092029190911773ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116179055506110ac9050565b60408101516000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039092169190911790555b8060c001516001600160801b031683602001516001600160801b0316146110f25760c0810151600480546001600160801b0319166001600160801b039092169190911790555b8a1561114257608081015160015560a08101516001600160801b03161561113d5760a0810151600380546001600160801b031981166001600160801b03918216909301169190911790555b611188565b608081015160025560a08101516001600160801b0316156111885760a0810151600380546001600160801b03808216600160801b92839004821690940116029190911790555b8115158b1515146111a157602081015181518b036111ae565b80600001518a0381602001515b90965094508a156112e75760008512156111f0576111f07f00000000000000000000000000000000000000000000000000000000000000008d87600003613b86565b60006111fa613cd4565b9050336001600160a01b031663fa461e3388888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561127e57600080fd5b505af1158015611292573d6000803e3d6000fd5b5050505061129e613cd4565b6112a88289613e0d565b11156112e1576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b50611411565b600086121561131e5761131e7f00000000000000000000000000000000000000000000000000000000000000008d88600003613b86565b6000611328613e1d565b9050336001600160a01b031663fa461e3388888c8c6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156113ac57600080fd5b505af11580156113c0573d6000803e3d6000fd5b505050506113cc613e1d565b6113d68288613e0d565b111561140f576040805162461bcd60e51b815260206004820152600360248201526249494160e81b604482015290519081900360640190fd5b505b60408082015160c083015160608085015184518b8152602081018b90526001600160a01b03948516818701526001600160801b039093169183019190915260020b60808201529151908e169133917fc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca679181900360a00190a350506000805460ff60f01b1916600160f01b17905550919890975095505050505050565b6004546001600160801b031681565b6003546001600160801b0380821691600160801b90041682565b60088161ffff81106114e757600080fd5b015463ffffffff81169150640100000000810460060b90600160581b81046001600160a01b031690600160f81b900460ff1684565b600054600160f01b900460ff16611560576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b19169055611575612bf0565b60008054600160d81b900461ffff169061159160088385613eb5565b6000805461ffff808416600160d81b810261ffff60d81b19909316929092179092559192508316146115fe576040805161ffff80851682528316602082015281517fac49e518f90a358f652e4400164f05a5d8f7e35e7747279bc3a93dbf584e125a929181900390910190a15b50506000805460ff60f01b1916600160f01b17905550565b6000546001600160a01b03811690600160a01b810460020b9061ffff600160b81b8204811691600160c81b8104821691600160d81b8204169060ff600160e81b8204811691600160f01b90041687565b600080548190600160f01b900460ff166116ad576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b191690556001600160801b0385166116cd57600080fd5b60008061171b60405180608001604052808c6001600160a01b031681526020018b60020b81526020018a60020b81526020016117118a6001600160801b0316613f58565b600f0b9052613f69565b9250925050819350809250600080600086111561173d5761173a613cd4565b91505b841561174e5761174b613e1d565b90505b336001600160a01b031663d348799787878b8b6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156117d057600080fd5b505af11580156117e4573d6000803e3d6000fd5b50505050600086111561183b576117f9613cd4565b6118038388613e0d565b111561183b576040805162461bcd60e51b815260206004820152600260248201526104d360f41b604482015290519081900360640190fd5b841561188b57611849613e1d565b6118538287613e0d565b111561188b576040805162461bcd60e51b81526020600482015260026024820152614d3160f01b604482015290519081900360640190fd5b8960020b8b60020b8d6001600160a01b03167f7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde338d8b8b60405180856001600160a01b03168152602001846001600160801b0316815260200183815260200182815260200194505050505060405180910390a450506000805460ff60f01b1916600160f01b17905550919890975095505050505050565b60025481565b600054600160f01b900460ff1661196c576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b19169055611981612bf0565b6004546001600160801b0316806119c3576040805162461bcd60e51b81526020600482015260016024820152601360fa1b604482015290519081900360640190fd5b60006119f8867f000000000000000000000000000000000000000000000000000000000000000062ffffff16620f42406141a9565b90506000611a2f867f000000000000000000000000000000000000000000000000000000000000000062ffffff16620f42406141a9565b90506000611a3b613cd4565b90506000611a47613e1d565b90508815611a7a57611a7a7f00000000000000000000000000000000000000000000000000000000000000008b8b613b86565b8715611aab57611aab7f00000000000000000000000000000000000000000000000000000000000000008b8a613b86565b336001600160a01b031663e9cbafb085858a8a6040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b158015611b2d57600080fd5b505af1158015611b41573d6000803e3d6000fd5b505050506000611b4f613cd4565b90506000611b5b613e1d565b905081611b688588613e0d565b1115611ba0576040805162461bcd60e51b8152602060048201526002602482015261046360f41b604482015290519081900360640190fd5b80611bab8487613e0d565b1115611be3576040805162461bcd60e51b8152602060048201526002602482015261463160f01b604482015290519081900360640190fd5b8382038382038115611c725760008054600160e81b9004600f16908115611c16578160ff168481611c1057fe5b04611c19565b60005b90506001600160801b03811615611c4c57600380546001600160801b038082168401166001600160801b03199091161790555b611c66818503600160801b8d6001600160801b03166132d9565b60018054909101905550505b8015611cfd5760008054600160e81b900460041c600f16908115611ca2578160ff168381611c9c57fe5b04611ca5565b60005b90506001600160801b03811615611cd757600380546001600160801b03600160801b8083048216850182160291161790555b611cf1818403600160801b8d6001600160801b03166132d9565b60028054909101905550505b8d6001600160a01b0316336001600160a01b03167fbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca6338f8f86866040518085815260200184815260200183815260200182815260200194505050505060405180910390a350506000805460ff60f01b1916600160f01b179055505050505050505050505050565b600080548190600160f01b900460ff16611dca576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b19168155611de460073389896141e3565b60038101549091506001600160801b0390811690861611611e055784611e14565b60038101546001600160801b03165b60038201549093506001600160801b03600160801b909104811690851611611e3c5783611e52565b6003810154600160801b90046001600160801b03165b91506001600160801b03831615611eb7576003810180546001600160801b031981166001600160801b03918216869003821617909155611eb7907f0000000000000000000000000000000000000000000000000000000000000000908a908616613b86565b6001600160801b03821615611f1d576003810180546001600160801b03600160801b808304821686900382160291811691909117909155611f1d907f0000000000000000000000000000000000000000000000000000000000000000908a908516613b86565b604080516001600160a01b038a1681526001600160801b0380861660208301528416818301529051600288810b92908a900b9133917f70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0919081900360600190a4506000805460ff60f01b1916600160f01b17905590969095509350505050565b60076020526000908152604090208054600182015460028301546003909301546001600160801b0392831693919281811691600160801b90041685565b60066020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b600054600160f01b900460ff16612054576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b1916905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b1580156120c157600080fd5b505afa1580156120d5573d6000803e3d6000fd5b505050506040513d60208110156120eb57600080fd5b50516001600160a01b0316331461210157600080fd5b60ff82161580612124575060048260ff16101580156121245750600a8260ff1611155b801561214e575060ff8116158061214e575060048160ff161015801561214e5750600a8160ff1611155b61215757600080fd5b60008054610ff0600484901b16840160ff908116600160e81b9081027fffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841617909355919004167f973d8d92bb299f4af6ce49b52a8adb85ae46b9f214c4c4fc06ac77401237b1336010826040805160ff9390920683168252600f600486901c16602083015286831682820152918516606082015290519081900360800190a150506000805460ff60f01b1916600160f01b17905550565b600080548190600160f01b900460ff16612256576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b1916905560408051638da5cb5b60e01b815290516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691638da5cb5b916004808301926020929190829003018186803b1580156122c357600080fd5b505afa1580156122d7573d6000803e3d6000fd5b505050506040513d60208110156122ed57600080fd5b50516001600160a01b0316331461230357600080fd5b6003546001600160801b039081169085161161231f578361232c565b6003546001600160801b03165b6003549092506001600160801b03600160801b9091048116908416116123525782612366565b600354600160801b90046001600160801b03165b90506001600160801b038216156123e7576003546001600160801b038381169116141561239557600019909101905b600380546001600160801b031981166001600160801b039182168590038216179091556123e7907f00000000000000000000000000000000000000000000000000000000000000009087908516613b86565b6001600160801b0381161561246d576003546001600160801b03828116600160801b90920416141561241857600019015b600380546001600160801b03600160801b80830482168590038216029181169190911790915561246d907f00000000000000000000000000000000000000000000000000000000000000009087908416613b86565b604080516001600160801b0380851682528316602082015281516001600160a01b0388169233927f596b573906218d3411850b26a6b437d6c4522fdb43d2d2386263f86d50b8b151929081900390910190a36000805460ff60f01b1916600160f01b1790559094909350915050565b6060806124e7612bf0565b61255e6124f2612c27565b858580806020026020016040519081016040528093929190818152602001838360200280828437600092018290525054600454600896959450600160a01b820460020b935061ffff600160b81b8304811693506001600160801b0390911691600160c81b900416614247565b915091509250929050565b600080548190600160f01b900460ff166125b0576040805162461bcd60e51b81526020600482015260036024820152624c4f4b60e81b604482015290519081900360640190fd5b6000805460ff60f01b1916815560408051608081018252338152600288810b602083015287900b918101919091528190819061260990606081016125fc6001600160801b038a16613f58565b600003600f0b9052613f69565b925092509250816000039450806000039350600085118061262a5750600084115b15612669576003830180546001600160801b038082168089018216600160801b93849004831689019092169092029091176001600160801b0319161790555b604080516001600160801b0388168152602081018790528082018690529051600289810b92908b900b9133917f0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c919081900360600190a450506000805460ff60f01b1916600160f01b179055509094909350915050565b60008060006126ed612bf0565b6126f785856143a1565b600285810b810b60009081526005602052604080822087840b90930b825281206003830154600681900b9367010000000000000082046001600160a01b0316928492600160d81b810463ffffffff169284929091600160f81b900460ff168061275f57600080fd5b6003820154600681900b985067010000000000000081046001600160a01b03169650600160d81b810463ffffffff169450600160f81b900460ff16806127a457600080fd5b50506040805160e0810182526000546001600160a01b0381168252600160a01b8104600290810b810b810b6020840181905261ffff600160b81b8404811695850195909552600160c81b830485166060850152600160d81b8304909416608084015260ff600160e81b8304811660a0850152600160f01b909204909116151560c08301529093508e810b91900b1215905061284d575093909403965090039350900390506128d0565b8a60020b816020015160020b12156128c1576000612869612c27565b602083015160408401516004546060860151939450600093849361289f936008938893879392916001600160801b031690613389565b9a9003989098039b5050949096039290920396509091030392506128d0915050565b50949093039650039350900390505b9250925092565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60015481565b60056020526000908152604090208054600182015460028301546003909301546001600160801b03831693600160801b909304600f0b9290600681900b9067010000000000000081046001600160a01b031690600160d81b810463ffffffff1690600160f81b900460ff1688565b6000546001600160a01b031615612a1e576040805162461bcd60e51b8152602060048201526002602482015261414960f01b604482015290519081900360640190fd5b6000612a29826136a5565b9050600080612a41612a39612c27565b60089061446a565b6040805160e0810182526001600160a01b038816808252600288810b6020808501829052600085870181905261ffff898116606088018190529089166080880181905260a08801839052600160c0909801979097528154600160f01b73ffffffffffffffffffffffffffffffffffffffff19909116871762ffffff60a01b1916600160a01b62ffffff9787900b9790971696909602959095177fffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffff16600160c81b9091021761ffff60d81b1916600160d81b909602959095177fff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1692909217909355835191825281019190915281519395509193507f98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c9592918290030190a150505050565b60008082600281900b620d89e71981612b9957fe5b05029050600083600281900b620d89e881612bb057fe5b0502905060008460020b83830360020b81612bc757fe5b0560010190508062ffffff166001600160801b03801681612be457fe5b0493505050505b919050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612c2557600080fd5b565b4290565b60008060008460020b8660020b81612c3f57fe5b05905060008660020b128015612c6657508460020b8660020b81612c5f57fe5b0760020b15155b15612c7057600019015b8315612ce557600080612c82836144b6565b600182810b810b600090815260208d9052604090205460ff83169190911b80016000190190811680151597509294509092509085612cc757888360ff16860302612cda565b88612cd1826144c8565b840360ff168603025b965050505050612d63565b600080612cf4836001016144b6565b91509150600060018260ff166001901b031990506000818b60008660010b60010b8152602001908152602001600020541690508060001415955085612d4657888360ff0360ff16866001010102612d5c565b8883612d5183614568565b0360ff168660010101025b9650505050505b5094509492505050565b60008060008360020b12612d84578260020b612d8c565b8260020b6000035b9050620d89e8811115612dca576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b600060018216612dde57600160801b612df0565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615612e24576ffff97272373d413259a46990580e213a0260801c5b6004821615612e43576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615612e62576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615612e81576fffcb9843d60f6159c9db58835c9266440260801c5b6020821615612ea0576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615612ebf576fff2ea16466c96a3843ec78b326b528610260801c5b6080821615612ede576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615612efe576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615612f1e576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615612f3e576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615612f5e576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615612f7e576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615612f9e576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615612fbe576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615612fde576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615612fff576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b6202000082161561301f576e5d6af8dedb81196699c329225ee6040260801c5b6204000082161561303e576d2216e584f5fa1ea926041bedfe980260801c5b6208000082161561305b576b048a170391f7dc42444e8fa20260801c5b60008460020b131561307657806000198161307257fe5b0490505b64010000000081061561308a57600161308d565b60005b60ff16602082901c0192505050919050565b60008080806001600160a01b03808916908a1610158187128015906131245760006130d88989620f42400362ffffff16620f42406132d9565b9050826130f1576130ec8c8c8c6001614652565b6130fe565b6130fe8b8d8c60016146cd565b955085811061310f578a965061311e565b61311b8c8b838661478a565b96505b5061316e565b8161313b576131368b8b8b60006146cd565b613148565b6131488a8c8b6000614652565b935083886000031061315c5789955061316e565b61316b8b8a8a600003856147d6565b95505b6001600160a01b038a81169087161482156131d15780801561318d5750815b6131a35761319e878d8c60016146cd565b6131a5565b855b95508080156131b2575081155b6131c8576131c3878d8c6000614652565b6131ca565b845b945061321b565b8080156131db5750815b6131f1576131ec8c888c6001614652565b6131f3565b855b9550808015613200575081155b613216576132118c888c60006146cd565b613218565b845b94505b8115801561322b57508860000385115b15613237578860000394505b81801561325657508a6001600160a01b0316876001600160a01b031614155b15613265578589039350613282565b61327f868962ffffff168a620f42400362ffffff166141a9565b93505b50505095509550955095915050565b6000600160ff1b82106132a357600080fd5b5090565b808203828113156000831215146132bd57600080fd5b92915050565b818101828112156000831215146132bd57600080fd5b600080806000198587098686029250828110908390030390508061330f576000841161330457600080fd5b508290049050613382565b80841161331b57600080fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150505b9392505050565b60008063ffffffff8716613430576000898661ffff1661ffff81106133aa57fe5b60408051608081018252919092015463ffffffff8082168084526401000000008304600690810b810b900b6020850152600160581b83046001600160a01b031694840194909452600160f81b90910460ff16151560608301529092508a161461341c57613419818a8988614822565b90505b806020015181604001519250925050613510565b8688036000806134458c8c858c8c8c8c6148d2565b91509150816000015163ffffffff168363ffffffff161415613477578160200151826040015194509450505050613510565b805163ffffffff8481169116141561349f578060200151816040015194509450505050613510565b8151815160208085015190840151918390039286039163ffffffff80841692908516910360060b816134cd57fe5b05028460200151018263ffffffff168263ffffffff1686604001518660400151036001600160a01b031602816134ff57fe5b048560400151019650965050505050505b97509795505050505050565b600295860b860b60009081526020979097526040909620600181018054909503909455938301805490920390915560038201805463ffffffff600160d81b6001600160a01b036701000000000000008085048216909603169094027fffffffffff0000000000000000000000000000000000000000ffffffffffffff90921691909117600681810b90960390950b66ffffffffffffff1666ffffffffffffff199095169490941782810485169095039093160263ffffffff60d81b1990931692909217905554600160801b9004600f0b90565b60008082600f0b121561365457826001600160801b03168260000384039150816001600160801b03161061364f576040805162461bcd60e51b81526020600482015260026024820152614c5360f01b604482015290519081900360640190fd5b6132bd565b826001600160801b03168284019150816001600160801b031610156132bd576040805162461bcd60e51b81526020600482015260026024820152614c4160f01b604482015290519081900360640190fd5b60006401000276a36001600160a01b038316108015906136e1575073fffd8963efd1fc6a506488495d951d5263988d266001600160a01b038316105b613716576040805162461bcd60e51b81526020600482015260016024820152602960f91b604482015290519081900360640190fd5b77ffffffffffffffffffffffffffffffffffffffff00000000602083901b166001600160801b03811160071b81811c67ffffffffffffffff811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211600190811b92831c979088119617909417909217179091171717608081106137b757607f810383901c91506137c1565b80607f0383901b91505b908002607f81811c60ff83811c9190911c800280831c81831c1c800280841c81841c1c800280851c81851c1c800280861c81861c1c800280871c81871c1c800280881c81881c1c800280891c81891c1c8002808a1c818a1c1c8002808b1c818b1c1c8002808c1c818c1c1c8002808d1c818d1c1c8002808e1c9c81901c9c909c1c80029c8d901c9e9d607f198f0160401b60c09190911c678000000000000000161760c19b909b1c674000000000000000169a909a1760c29990991c672000000000000000169890981760c39790971c671000000000000000169690961760c49590951c670800000000000000169490941760c59390931c670400000000000000169290921760c69190911c670200000000000000161760c79190911c670100000000000000161760c89190911c6680000000000000161760c99190911c6640000000000000161760ca9190911c6620000000000000161760cb9190911c6610000000000000161760cc9190911c6608000000000000161760cd9190911c66040000000000001617693627a301d71055774c8581026f028f6481ab7f045a5af012a19d003aa9198101608090811d906fdb2df09e81959a81455e260799a0632f8301901d600281810b9083900b146139c257886001600160a01b03166139a682612d6d565b6001600160a01b031611156139bb57816139bd565b805b6139c4565b815b9998505050505050505050565b6000806000898961ffff1661ffff81106139e757fe5b60408051608081018252919092015463ffffffff8082168084526401000000008304600690810b810b900b6020850152600160581b83046001600160a01b031694840194909452600160f81b90910460ff161515606083015290925089161415613a575788859250925050613510565b8461ffff168461ffff16118015613a7857506001850361ffff168961ffff16145b15613a8557839150613a89565b8491505b8161ffff168960010161ffff1681613a9d57fe5b069250613aac81898989614822565b8a8461ffff1661ffff8110613abd57fe5b825191018054602084015160408501516060909501511515600160f81b027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001600160a01b03909616600160581b027fff0000000000000000000000000000000000000000ffffffffffffffffffffff60069390930b66ffffffffffffff16640100000000026affffffffffffff000000001963ffffffff90971663ffffffff199095169490941795909516929092171692909217929092161790555097509795505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b60208310613c025780518252601f199092019160209182019101613be3565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613c64576040519150601f19603f3d011682016040523d82523d6000602084013e613c69565b606091505b5091509150818015613c97575080511580613c975750808060200190516020811015613c9457600080fd5b50515b613ccd576040805162461bcd60e51b81526020600482015260026024820152612a2360f11b604482015290519081900360640190fd5b5050505050565b604080513060248083019190915282518083039091018152604490910182526020810180516001600160e01b03166370a0823160e01b17815291518151600093849384936001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693919290918291908083835b60208310613d6d5780518252601f199092019160209182019101613d4e565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613dcd576040519150601f19603f3d011682016040523d82523d6000602084013e613dd2565b606091505b5091509150818015613de657506020815110155b613def57600080fd5b808060200190516020811015613e0457600080fd5b50519250505090565b808201828110156132bd57600080fd5b604080513060248083019190915282518083039091018152604490910182526020810180516001600160e01b03166370a0823160e01b17815291518151600093849384936001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016939192909182919080838360208310613d6d5780518252601f199092019160209182019101613d4e565b6000808361ffff1611613ef3576040805162461bcd60e51b81526020600482015260016024820152604960f81b604482015290519081900360640190fd5b8261ffff168261ffff1611613f09575081613382565b825b8261ffff168161ffff161015613f4f576001858261ffff1661ffff8110613f2e57fe5b01805463ffffffff191663ffffffff92909216919091179055600101613f0b565b50909392505050565b80600f81900b8114612beb57600080fd5b6000806000613f76612bf0565b613f88846020015185604001516143a1565b6040805160e0810182526000546001600160a01b0381168252600160a01b8104600290810b810b900b602080840182905261ffff600160b81b8404811685870152600160c81b84048116606080870191909152600160d81b8504909116608086015260ff600160e81b8504811660a0870152600160f01b909404909316151560c08501528851908901519489015192890151939461402c9491939092909190614acf565b93508460600151600f0b6000146141a157846020015160020b816020015160020b12156140815761407a6140638660200151612d6d565b6140708760400151612d6d565b8760600151614c84565b92506141a1565b846040015160020b816020015160020b12156141775760045460408201516001600160801b03909116906140d3906140b7612c27565b60208501516060860151608087015160089493929187916139d1565b6000805461ffff60c81b1916600160c81b61ffff938416021761ffff60b81b1916600160b81b939092169290920217905581516040870151614123919061411990612d6d565b8860600151614c84565b93506141416141358760200151612d6d565b83516060890151614cc8565b92506141518187606001516135ef565b600480546001600160801b0319166001600160801b0392909216919091179055506141a1565b61419e6141878660200151612d6d565b6141948760400151612d6d565b8760600151614cc8565b91505b509193909250565b60006141b68484846132d9565b9050600082806141c257fe5b84860911156133825760001981106141d957600080fd5b6001019392505050565b6040805160609490941b6bffffffffffffffffffffffff1916602080860191909152600293840b60e890811b60348701529290930b90911b60378401528051808403601a018152603a90930181528251928201929092206000908152929052902090565b60608060008361ffff1611614287576040805162461bcd60e51b81526020600482015260016024820152604960f81b604482015290519081900360640190fd5b865167ffffffffffffffff8111801561429f57600080fd5b506040519080825280602002602001820160405280156142c9578160200160208202803683370190505b509150865167ffffffffffffffff811180156142e457600080fd5b5060405190808252806020026020018201604052801561430e578160200160208202803683370190505b50905060005b87518110156143945761433f8a8a8a848151811061432e57fe5b60200260200101518a8a8a8a613389565b84838151811061434b57fe5b6020026020010184848151811061435e57fe5b60200260200101826001600160a01b03166001600160a01b03168152508260060b60060b81525050508080600101915050614314565b5097509795505050505050565b8060020b8260020b126143e1576040805162461bcd60e51b8152602060048201526003602482015262544c5560e81b604482015290519081900360640190fd5b620d89e719600283900b1215614424576040805162461bcd60e51b8152602060048201526003602482015262544c4d60e81b604482015290519081900360640190fd5b620d89e8600282900b1315614466576040805162461bcd60e51b815260206004820152600360248201526254554d60e81b604482015290519081900360640190fd5b5050565b6040805160808101825263ffffffff9283168082526000602083018190529282019290925260016060909101819052835463ffffffff1916909117909116600160f81b17909155908190565b60020b600881901d9161010090910790565b60008082116144d657600080fd5b600160801b82106144e957608091821c91015b68010000000000000000821061450157604091821c91015b640100000000821061451557602091821c91015b62010000821061452757601091821c91015b610100821061453857600891821c91015b6010821061454857600491821c91015b6004821061455857600291821c91015b60028210612beb57600101919050565b600080821161457657600080fd5b5060ff6001600160801b0382161561459157607f1901614599565b608082901c91505b67ffffffffffffffff8216156145b257603f19016145ba565b604082901c91505b63ffffffff8216156145cf57601f19016145d7565b602082901c91505b61ffff8216156145ea57600f19016145f2565b601082901c91505b60ff821615614604576007190161460c565b600882901c91505b600f82161561461e5760031901614626565b600482901c91505b60038216156146385760011901614640565b600282901c91505b6001821615612beb5760001901919050565b6000836001600160a01b0316856001600160a01b03161115614672579293925b8161469f5761469a836001600160801b03168686036001600160a01b0316600160601b6132d9565b6146c2565b6146c2836001600160801b03168686036001600160a01b0316600160601b6141a9565b90505b949350505050565b6000836001600160a01b0316856001600160a01b031611156146ed579293925b7bffffffffffffffffffffffffffffffff000000000000000000000000606084901b166001600160a01b03868603811690871661472957600080fd5b8361475957866001600160a01b031661474c8383896001600160a01b03166132d9565b8161475357fe5b0461477f565b61477f6147708383896001600160a01b03166141a9565b886001600160a01b0316614cf7565b979650505050505050565b600080856001600160a01b0316116147a157600080fd5b6000846001600160801b0316116147b757600080fd5b816147c95761469a8585856001614d02565b6146c28585856001614de3565b600080856001600160a01b0316116147ed57600080fd5b6000846001600160801b03161161480357600080fd5b816148155761469a8585856000614de3565b6146c28585856000614d02565b61482a61564a565b600085600001518503905060405180608001604052808663ffffffff1681526020018263ffffffff168660020b0288602001510160060b81526020016000856001600160801b03161161487e576001614880565b845b6001600160801b031673ffffffff00000000000000000000000000000000608085901b16816148ab57fe5b048860400151016001600160a01b0316815260200160011515815250915050949350505050565b6148da61564a565b6148e261564a565b888561ffff1661ffff81106148f357fe5b60408051608081018252919092015463ffffffff81168083526401000000008204600690810b810b900b6020840152600160581b82046001600160a01b031693830193909352600160f81b900460ff1615156060820152925061495890899089614ed8565b15614990578663ffffffff16826000015163ffffffff16141561497a57613510565b8161498783898988614822565b91509150613510565b888361ffff168660010161ffff16816149a557fe5b0661ffff1661ffff81106149b557fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b60208401526001600160a01b03600160581b8204169183019190915260ff600160f81b90910416151560608201819052909250614a6c57604080516080810182528a5463ffffffff811682526401000000008104600690810b810b900b6020830152600160581b81046001600160a01b031692820192909252600160f81b90910460ff161515606082015291505b614a7b88836000015189614ed8565b614ab2576040805162461bcd60e51b815260206004820152600360248201526213d31160ea1b604482015290519081900360640190fd5b614abf8989898887614f9b565b9150915097509795505050505050565b6000614ade60078787876141e3565b60015460025491925090600080600f87900b15614c24576000614aff612c27565b6000805460045492935090918291614b499160089186918591600160a01b810460020b9161ffff600160b81b83048116926001600160801b0390921691600160c81b900416613389565b9092509050614b8360058d8b8d8b8b87898b60007f000000000000000000000000000000000000000000000000000000000000000061513b565b9450614bba60058c8b8d8b8b87898b60017f000000000000000000000000000000000000000000000000000000000000000061513b565b93508415614bee57614bee60068d7f0000000000000000000000000000000000000000000000000000000000000000615325565b8315614c2057614c2060068c7f0000000000000000000000000000000000000000000000000000000000000000615325565b5050505b600080614c3660058c8c8b8a8a61538b565b9092509050614c47878a8484615437565b600089600f0b1215614c75578315614c6457614c6460058c6155cc565b8215614c7557614c7560058b6155cc565b50505050505095945050505050565b60008082600f0b12614caa57614ca5614ca085858560016146cd565b613291565b6146c5565b614cbd614ca085858560000360006146cd565b600003949350505050565b60008082600f0b12614ce457614ca5614ca08585856001614652565b614cbd614ca08585856000036000614652565b808204910615150190565b60008115614d755760006001600160a01b03841115614d3857614d3384600160601b876001600160801b03166132d9565b614d50565b6001600160801b038516606085901b81614d4e57fe5b045b9050614d6d614d686001600160a01b03881683613e0d565b6155f8565b9150506146c5565b60006001600160a01b03841115614da357614d9e84600160601b876001600160801b03166141a9565b614dba565b614dba606085901b6001600160801b038716614cf7565b905080866001600160a01b031611614dd157600080fd5b6001600160a01b0386160390506146c5565b600082614df15750836146c5565b7bffffffffffffffffffffffffffffffff000000000000000000000000606085901b168215614e91576001600160a01b03861684810290858281614e3157fe5b041415614e6257818101828110614e6057614e5683896001600160a01b0316836141a9565b93505050506146c5565b505b614e8882614e83878a6001600160a01b03168681614e7c57fe5b0490613e0d565b614cf7565b925050506146c5565b6001600160a01b03861684810290858281614ea857fe5b04148015614eb557508082115b614ebe57600080fd5b808203614e56614d68846001600160a01b038b16846141a9565b60008363ffffffff168363ffffffff1611158015614f0257508363ffffffff168263ffffffff1611155b15614f1e578163ffffffff168363ffffffff1611159050613382565b60008463ffffffff168463ffffffff1611614f46578363ffffffff1664010000000001614f4e565b8363ffffffff165b64ffffffffff16905060008563ffffffff168463ffffffff1611614f7f578363ffffffff1664010000000001614f87565b8363ffffffff165b64ffffffffff169091111595945050505050565b614fa361564a565b614fab61564a565b60008361ffff168560010161ffff1681614fc157fe5b0661ffff169050600060018561ffff16830103905060005b506002818301048961ffff87168281614fee57fe5b0661ffff8110614ffa57fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b60208401526001600160a01b03600160581b8204169183019190915260ff600160f81b9091041615156060820181905290955061506557806001019250614fd9565b898661ffff16826001018161507657fe5b0661ffff811061508257fe5b60408051608081018252929091015463ffffffff811683526401000000008104600690810b810b900b60208401526001600160a01b03600160581b8204169183019190915260ff600160f81b909104161515606082015285519094506000906150ed908b908b614ed8565b905080801561510657506151068a8a8760000151614ed8565b15615111575061512e565b8061512157600182039250615128565b8160010193505b50614fd9565b5050509550959350505050565b60028a810b900b600090815260208c90526040812080546001600160801b031682615166828d6135ef565b9050846001600160801b0316816001600160801b031611156151b4576040805162461bcd60e51b81526020600482015260026024820152614c4f60f01b604482015290519081900360640190fd5b6001600160801b03828116159082161581141594501561528a578c60020b8e60020b1361525a57600183018b9055600283018a90556003830180547fffffffffff0000000000000000000000000000000000000000ffffffffffffff166701000000000000006001600160a01b038c16021766ffffffffffffff191666ffffffffffffff60068b900b161763ffffffff60d81b1916600160d81b63ffffffff8a16021790555b6003830180547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160f81b1790555b82546001600160801b0319166001600160801b038216178355856152d35782546152ce906152c990600160801b9004600f90810b810b908f900b6132c3565b613f58565b6152f4565b82546152f4906152c990600160801b9004600f90810b810b908f900b6132a7565b8354600f9190910b6001600160801b03908116600160801b0291161790925550909c9b505050505050505050505050565b8060020b8260020b8161533457fe5b0760020b1561534257600080fd5b60008061535d8360020b8560020b8161535757fe5b056144b6565b600191820b820b60009081526020979097526040909620805460ff9097169190911b90951890945550505050565b600285810b80820b60009081526020899052604080822088850b850b83529082209193849391929184918291908a900b126153d1575050600182015460028301546153e4565b8360010154880391508360020154870390505b6000808b60020b8b60020b121561540657505060018301546002840154615419565b84600101548a0391508460020154890390505b92909803979097039b96909503949094039850939650505050505050565b6040805160a08101825285546001600160801b0390811682526001870154602083015260028701549282019290925260038601548083166060830152600160801b900490911660808201526000600f85900b6154d65781516001600160801b03166154ce576040805162461bcd60e51b815260206004820152600260248201526104e560f41b604482015290519081900360640190fd5b5080516154e5565b81516154e290866135ef565b90505b60006155098360200151860384600001516001600160801b0316600160801b6132d9565b9050600061552f8460400151860385600001516001600160801b0316600160801b6132d9565b905086600f0b6000146155565787546001600160801b0319166001600160801b0384161788555b60018801869055600288018590556001600160801b03821615158061558457506000816001600160801b0316115b156155c2576003880180546001600160801b031981166001600160801b039182168501821617808216600160801b9182900483168501909216021790555b5050505050505050565b600290810b810b6000908152602092909252604082208281556001810183905590810182905560030155565b806001600160a01b0381168114612beb57600080fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040805160808101825260008082526020820181905291810182905260608101919091529056fea164736f6c6343000706000a" + } + ], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": "0xcE2c49b75fF7a0C5f45EffDB2b6A776e60A0deCb", + "cumulativeGasUsed": "0x52362e", + "gasUsed": "0x52362e", + "contractAddress": null, + "logs": [ + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb" + ], + "data": "0x0000000000000000000000000000000000000002863c1f5cdae42f9540000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x1", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x0000000000000000000000001238536071e1c677a632429e3655c799b22cda52" + ], + "data": "0x00000000000000000000000000000000000000026c62ad77dc602dae00000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x2", + "removed": false + }, + { + "address": "0x0227628f3F023bb0B980b67D528571c95c6DaC1c", + "topics": [ + "0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118", + "0x000000000000000000000000153a1635ab52e15969fd65a72594a9875d87fcfd", + "0x000000000000000000000000fff9976782d46cc05630d1f6ebab18b2324d6b14", + "0x0000000000000000000000000000000000000000000000000000000000002710" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000c80000000000000000000000009cbac168dda0f817d1afa06bca721902ac8c364e", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x3", + "removed": false + }, + { + "address": "0x9CBAc168ddA0f817D1AFA06BCa721902AC8C364e", + "topics": [ + "0x98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95" + ], + "data": "0x0000000000000000000000000000000000000000000109443ac9bc7d1f96691cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca04d", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x4", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x0000000000000000000000009cbac168dda0f817d1afa06bca721902ac8c364e" + ], + "data": "0x00000000000000000000000000000000000000026c62ad77dc602dadffffae12", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x5", + "removed": false + }, + { + "address": "0x9CBAc168ddA0f817D1AFA06BCa721902AC8C364e", + "topics": [ + "0x7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde", + "0x0000000000000000000000001238536071e1c677a632429e3655c799b22cda52", + "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca310", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x0000000000000000000000001238536071e1c677a632429e3655c799b22cda520000000000000000000000000000000000000000000299fb7dca14626ce38c7600000000000000000000000000000000000000026c62ad77dc602dadffffae120000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x6", + "removed": false + }, + { + "address": "0x1238536071E1c677A632429e3655c799b22cDA52", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x00000000000000000000000000000000000000000000000000000000000034c3" + ], + "data": "0x", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x7", + "removed": false + }, + { + "address": "0x1238536071E1c677A632429e3655c799b22cDA52", + "topics": [ + "0x3067048beee31b25b2f1681f88dac838c8bba36af25bfb2b7cf7473a5847e35f", + "0x00000000000000000000000000000000000000000000000000000000000034c3" + ], + "data": "0x0000000000000000000000000000000000000000000299fb7dca14626ce38c7600000000000000000000000000000000000000026c62ad77dc602dadffffae120000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x8", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb" + ], + "data": "0x000000000000000000000000000000000000000019d971e4fe8401e740000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x9", + "removed": false + }, + { + "address": "0xcE2c49b75fF7a0C5f45EffDB2b6A776e60A0deCb", + "topics": [ + "0x0d1d2eaa1e5bac93f8aaaed98c5de5ec54cdeb3867b3238f239475dfb0dd337d", + "0x000000000000000000000000153a1635ab52e15969fd65a72594a9875d87fcfd", + "0x00000000000000000000000000000000000000000000000000000000000034c3", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xa", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef" + ], + "data": "0x000000000000000000000000000000000000000019d971e4fe8401e740000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xb", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000ce2c49b75ff7a0c5f45effdb2b6a776e60a0decb", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef" + ], + "data": "0x000000000000000000000000000000000000000019d971e4fe8401e740000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xc", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef", + "0x0000000000000000000000000000000000000000000000000000000000000001" + ], + "data": "0x000000000000000000000000000000000000000006765c793fa10079d0000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xd", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef", + "0x0000000000000000000000000000000000000000000000000000000000000002" + ], + "data": "0x000000000000000000000000000000000000000006765c793fa10079d0000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xe", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef", + "0x0000000000000000000000000000000000000000000000000000000000000003" + ], + "data": "0x000000000000000000000000000000000000000006765c793fa10079d0000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0xf", + "removed": false + }, + { + "address": "0x153A1635Ab52E15969Fd65a72594A9875d87fCfD", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000009350f89e2d7b6e96ba730783c2d76137b045fef", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" + ], + "data": "0x000000000000000000000000000000000000000006765c793fa10079d0000000", + "blockHash": "0x5f2b0ac88cf6f3a9a1c4efc871a140ce0122be0055daa6ddb5a78e5dc78040eb", + "blockNumber": "0x560005", + "transactionHash": "0xf83afb3216988ac46d5cd7a29e71900e87fcab507d82399617ee9fdf5f8485aa", + "transactionIndex": "0x0", + "logIndex": "0x10", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x04000800011000000100100020000080000020000000000000000000000100000100000000000000000400000000000080000800020080000400000000240000000001000000000000000009010000000005000000040040000200000000000020000000222000000000000100000800080000000800000000000010000000000020000018000000010800010008000000000010000080000004000000400000020000000000000000010100000000000000000040800000000000002400480000000412100040200002080001020000202000002004000000000000800060000010400000000000000000000000000000000010008000000000000200000800", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e02" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1712343176, + "chain": 11155111, + "commit": "94af5ff" +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 8b04e77..299ac9a 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,4 +1,5 @@ [profile.default] +solc_version = "0.8.25" src = "src" out = "out" libs = ["lib"] @@ -10,7 +11,11 @@ remappings = [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "gaslite/=lib/gaslitedrop/contracts/src/", + "solady/=lib/solady/src/", + "~/=src/", ] [fmt] line_length = 100 tab_width = 4 +[fuzz] +runs = 40 \ No newline at end of file diff --git a/lib/solady b/lib/solady new file mode 160000 index 0000000..bb4b43b --- /dev/null +++ b/lib/solady @@ -0,0 +1 @@ +Subproject commit bb4b43b44bec3c5d42604c08904bca0442e0bc78 diff --git a/script/DeployMetalFunFactory.s.sol b/script/DeployMetalFunFactory.s.sol new file mode 100644 index 0000000..40caa96 --- /dev/null +++ b/script/DeployMetalFunFactory.s.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {Script, console} from "forge-std/Script.sol"; + +import {METAL_FUN_FACTORY_SALT} from "../src/Constants.sol"; +import {InstantLiquidityToken} from "../src/InstantLiquidityToken.sol"; +import {MetalFunFactory} from "../src/MetalFunFactory.sol"; + +contract DeployMetalFunFactory is Script { + function run() public returns (MetalFunFactory) { + return _run(vm.envAddress("OWNER")); + } + + function _run(address _owner) public returns (MetalFunFactory) { + vm.broadcast(); + MetalFunFactory metalFunFactory = new MetalFunFactory{salt: METAL_FUN_FACTORY_SALT}(_owner); + + console.log("MetalFunFactory", address(metalFunFactory)); + + return metalFunFactory; + } +} diff --git a/script/DeployMetalFunFactoryV2.s.sol b/script/DeployMetalFunFactoryV2.s.sol new file mode 100644 index 0000000..fa19bc6 --- /dev/null +++ b/script/DeployMetalFunFactoryV2.s.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {Script, console} from "forge-std/Script.sol"; + +import {METAL_FUN_FACTORY_SALT} from "../src/Constants.sol"; +import {InstantLiquidityToken} from "../src/InstantLiquidityToken.sol"; +import {MetalFunFactoryV2} from "../src/MetalFunFactoryV2.sol"; + +contract DeployMetalFunFactory is Script { + function run() public returns (MetalFunFactoryV2) { + return _run(vm.envAddress("OWNER")); + } + + function _run(address _owner) public returns (MetalFunFactoryV2) { + vm.broadcast(); + MetalFunFactoryV2 metalFunFactoryV2 = + new MetalFunFactoryV2{salt: METAL_FUN_FACTORY_SALT}(_owner); + + console.log("MetalFunFactory", address(metalFunFactoryV2)); + + return metalFunFactoryV2; + } +} diff --git a/script/DeployMetalFunToken.s.sol b/script/DeployMetalFunToken.s.sol new file mode 100644 index 0000000..235d445 --- /dev/null +++ b/script/DeployMetalFunToken.s.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {Script, console} from "forge-std/Script.sol"; +import {MetalFunFactory, InstantLiquidityToken} from "../src/MetalFunFactory.sol"; + +contract DeployMetalFunToken is Script { + function run() public { + vm.broadcast(); + _run(0x773fd11aFeFbcBc7EF98fC51030D99C9f5605904); + } + + function _run(address _factory) public returns (InstantLiquidityToken, uint256) { + MetalFunFactory factory = MetalFunFactory(_factory); + (InstantLiquidityToken token, uint256 lpTokenId) = factory.deploy("", ""); + + console.log("token", address(token)); + + return (token, lpTokenId); + } +} diff --git a/script/DeployMetalFunTokenV2.s.sol b/script/DeployMetalFunTokenV2.s.sol new file mode 100644 index 0000000..48f9062 --- /dev/null +++ b/script/DeployMetalFunTokenV2.s.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {Script, console} from "forge-std/Script.sol"; +import {MetalFunFactoryV2, InstantLiquidityToken} from "../src/MetalFunFactoryV2.sol"; + +contract DeployMetalFunTokenV2 is Script { + function run() public { + vm.broadcast(); + _run(0xf8DEF29fc89e1D212F7CCA91d7d3b9aee7258A01); + } + + function _run(address _factory) public returns (InstantLiquidityToken, uint256) { + MetalFunFactoryV2 factory = MetalFunFactoryV2(_factory); + (InstantLiquidityToken token, uint256 lpTokenId) = factory.deploy("", "", 0.01 ether, 1000_000_000, address(0), 0); + + console.log("token", address(token)); + + return (token, lpTokenId); + } +} \ No newline at end of file diff --git a/src/Constants.sol b/src/Constants.sol index b593dd9..d9a9208 100644 --- a/src/Constants.sol +++ b/src/Constants.sol @@ -14,3 +14,4 @@ uint256 constant OWNER_ALLOCATION = 8_000_000_000 ether; bytes32 constant LIQUIDITY_TOKEN_SALT = keccak256("INSTANT_LIQUIDITY_TOKEN_V3"); bytes32 constant TOKEN_FACTORY_SALT = keccak256("TOKEN_FACTORY_V3"); bytes32 constant TOKEN_FACTORYV2_SALT = keccak256("TOKEN_FACTORY_AIRDROP_VARIANT"); +bytes32 constant METAL_FUN_FACTORY_SALT = keccak256("METAL_FUN_FACTORY"); diff --git a/src/MetalFunFactory.sol b/src/MetalFunFactory.sol new file mode 100644 index 0000000..fa30750 --- /dev/null +++ b/src/MetalFunFactory.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {POOL_FEE} from "./Constants.sol"; +import {InstantLiquidityToken} from "./InstantLiquidityToken.sol"; +import {INonfungiblePositionManager} from "./TokenFactory.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; +import {console} from "forge-std/console.sol"; + +contract MetalFunFactory is Ownable, ERC721Holder { + uint256 immutable TOTAL_SUPPLY = 923_500_000 ether; + + error UNSUPPORTED_CHAIN(); + + event TokenFactoryDeployment( + address indexed token, + uint256 indexed tokenId, + address indexed recipient, + string name, + string symbol + ); + + struct Storage { + // a nonce to ensure unique token ids for each deployment + uint96 deploymentNonce; + // the instant liquidity token contract + InstantLiquidityToken instantLiquidityToken; + } + + Storage public s = Storage({ + deploymentNonce: 0, + instantLiquidityToken: InstantLiquidityToken(0xD74D14ebe305c93D023C966640788f05593F0fdE) + }); + + constructor(address _owner) Ownable(_owner) { + uint256 chainId = block.chainid; + + if ( + // mainnet + chainId != 1 + // goerli + && chainId != 5 + // arbitrum + && chainId != 42161 + // optimism + && chainId != 10 + // polygon + && chainId != 137 + // bnb + && chainId != 56 + // base + && chainId != 8453 + // base sepolia + && chainId != 84532 + // sepolia + && chainId != 11155111 + // zora + && chainId != 7777777 + // degen chain + && chainId != 666666666 + ) revert UNSUPPORTED_CHAIN(); + } + + /** + * @dev sourced from: https://docs.uniswap.org/contracts/v3/reference/deployments + */ + function _getAddresses() + internal + view + returns (address weth, INonfungiblePositionManager nonFungiblePositionManager) + { + uint256 chainId = block.chainid; + // Mainnet, Goerli, Arbitrum, Optimism, Polygon + nonFungiblePositionManager = + INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88); + + // mainnet + if (chainId == 1) weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + // goerli + if (chainId == 5) weth = 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6; + // arbitrum + if (chainId == 42161) weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1; + // optimism + if (chainId == 10) weth = 0x4200000000000000000000000000000000000006; + // polygon + if (chainId == 137) weth = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; + // bnb + if (chainId == 56) { + weth = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; + nonFungiblePositionManager = + INonfungiblePositionManager(0x7b8A01B39D58278b5DE7e48c8449c9f4F5170613); + } + // base + if (chainId == 8453) { + weth = 0x4200000000000000000000000000000000000006; + nonFungiblePositionManager = + INonfungiblePositionManager(0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1); + } + // base sepolia + if (chainId == 84532) { + weth = 0x4200000000000000000000000000000000000006; + nonFungiblePositionManager = + INonfungiblePositionManager(0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2); + } + // sepolia + if (chainId == 11155111) { + weth = 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14; + nonFungiblePositionManager = + INonfungiblePositionManager(0x1238536071E1c677A632429e3655c799b22cDA52); + } + // zora + if (chainId == 7777777) { + weth = 0x4200000000000000000000000000000000000006; + nonFungiblePositionManager = + INonfungiblePositionManager(0xbC91e8DfA3fF18De43853372A3d7dfe585137D78); + } + // degen chain + if (chainId == 666666666) { + // wrapped degen + weth = 0xEb54dACB4C2ccb64F8074eceEa33b5eBb38E5387; + nonFungiblePositionManager = + // proxy swap + INonfungiblePositionManager(0x56c65e35f2Dd06f659BCFe327C4D7F21c9b69C2f); + } + } + + function _getMintParams(address token, address weth) + internal + view + virtual + returns (INonfungiblePositionManager.MintParams memory params, uint160 initialSqrtPrice) + { + bool tokenIsLessThanWeth = token < weth; + (address token0, address token1) = tokenIsLessThanWeth ? (token, weth) : (weth, token); + (int24 tickLower, int24 tickUpper) = + tokenIsLessThanWeth ? (int24(-208400), int24(0)) : (int24(0), int24(208400)); + (uint256 amt0, uint256 amt1) = tokenIsLessThanWeth + ? (uint256(TOTAL_SUPPLY), uint256(0)) + : (uint256(0), uint256(TOTAL_SUPPLY)); + + params = INonfungiblePositionManager.MintParams({ + token0: token0, + token1: token1, + // 1% fee + fee: POOL_FEE, + tickLower: tickLower, + tickUpper: tickUpper, + amount0Desired: amt0, + // allow for a bit of slippage + amount0Min: amt0 - (amt0 / 1e8), + amount1Desired: amt1, + amount1Min: amt1 - (amt1 / 1e8), + deadline: block.timestamp, + recipient: address(this) + }); + + initialSqrtPrice = + tokenIsLessThanWeth ? 2363603296768335609331712 : 2655734041312737263542517807185920; + } + + function _deploy(string memory _name, string memory _symbol) + internal + returns (InstantLiquidityToken, uint256) + { + // get the addresses per-chain + (address weth, INonfungiblePositionManager nonfungiblePositionManager) = _getAddresses(); + address token; + { + Storage memory store = s; + // deploy and initialize a new token + token = Clones.cloneDeterministic( + address(store.instantLiquidityToken), + keccak256(abi.encode(block.chainid, store.deploymentNonce)) + ); + InstantLiquidityToken(token).initialize({ + _mintTo: address(this), + _totalSupply: TOTAL_SUPPLY, + _name: _name, + _symbol: _symbol + }); + s.deploymentNonce += 1; + } + + // sort the tokens and the amounts + (address token0, address token1) = token < weth ? (token, weth) : (weth, token); + + // approve the non-fungible position mgr for the pool liquidity amount + InstantLiquidityToken(token).approve({ + spender: address(nonfungiblePositionManager), + value: TOTAL_SUPPLY + }); + + (INonfungiblePositionManager.MintParams memory mintParams, uint160 initialSquareRootPrice) = + _getMintParams({token: token, weth: weth}); + + // create the pool + nonfungiblePositionManager.createAndInitializePoolIfNecessary({ + token0: token0, + token1: token1, + fee: POOL_FEE, + sqrtPriceX96: initialSquareRootPrice + }); + + // mint the position + (uint256 lpTokenId,,,) = nonfungiblePositionManager.mint({params: mintParams}); + + return (InstantLiquidityToken(token), lpTokenId); + } + + function deploy(string memory _name, string memory _symbol) + public + returns (InstantLiquidityToken token, uint256 lpTokenId) + { + (token, lpTokenId) = _deploy(_name, _symbol); + + emit TokenFactoryDeployment(address(token), lpTokenId, msg.sender, _name, _symbol); + } + + function collectFees(address _recipient, uint256[] memory _tokenIds) public onlyOwner { + (, INonfungiblePositionManager nonfungiblePositionManager) = _getAddresses(); + + for (uint256 i; i < _tokenIds.length; ++i) { + nonfungiblePositionManager.collect( + INonfungiblePositionManager.CollectParams({ + recipient: _recipient, + amount0Max: type(uint128).max, + amount1Max: type(uint128).max, + tokenId: _tokenIds[i] + }) + ); + } + } + + function setInstantLiquidityToken(address _instantLiquidityToken) public onlyOwner { + s.instantLiquidityToken = InstantLiquidityToken(_instantLiquidityToken); + } +} diff --git a/src/MetalFunFactoryV2.sol b/src/MetalFunFactoryV2.sol new file mode 100644 index 0000000..e6163df --- /dev/null +++ b/src/MetalFunFactoryV2.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {POOL_FEE} from "./Constants.sol"; +import {calculatePrices} from "./lib/priceCalc.sol"; +import {getAddresses} from "./lib/Addresses.sol"; +import {InstantLiquidityToken} from "./InstantLiquidityToken.sol"; +import {INonfungiblePositionManager} from "./TokenFactory.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; + +contract MetalFunFactoryV2 is Ownable, ERC721Holder { + error UNSUPPORTED_CHAIN(); + + event TokenFactoryDeployment( + address indexed token, + uint256 indexed tokenId, + address indexed recipient, + string name, + string symbol + ); + + struct Storage { + // a nonce to ensure unique token ids for each deployment. + uint96 deploymentNonce; + // the instant liquidity token contract. + InstantLiquidityToken instantLiquidityToken; + } + + Storage public s = Storage({ + deploymentNonce: 0, + instantLiquidityToken: InstantLiquidityToken(0xD74D14ebe305c93D023C966640788f05593F0fdE) + }); + + constructor(address _owner) Ownable(_owner) { + uint256 chainId = block.chainid; + + if ( + // mainnet + chainId != 1 + // goerli + && chainId != 5 + // arbitrum + && chainId != 42161 + // optimism + && chainId != 10 + // polygon + && chainId != 137 + // bnb + && chainId != 56 + // base + && chainId != 8453 + // base sepolia + && chainId != 84532 + // sepolia + && chainId != 11155111 + // zora + && chainId != 7777777 + // degen chain + && chainId != 666666666 + ) revert UNSUPPORTED_CHAIN(); + } + + function _getMintParams( + address token, + address weth, + uint256 initialPricePerEth, + uint256 liquidityIn + ) + internal + view + returns (INonfungiblePositionManager.MintParams memory params, uint160 initialSqrtPrice) + { + bool tokenIsLessThanWeth = token < weth; + + (address token0, address token1) = tokenIsLessThanWeth ? (token, weth) : (weth, token); + (uint160 sqrtPrice, int24 tickLower, int24 tickUpper) = + calculatePrices(token, weth, initialPricePerEth); + + (uint256 amt0, uint256 amt1) = tokenIsLessThanWeth + ? (uint256(liquidityIn), uint256(0)) + : (uint256(0), uint256(liquidityIn)); + + params = INonfungiblePositionManager.MintParams({ + token0: token0, + token1: token1, + // 1% fee + fee: 10_000, + tickLower: tickLower, + tickUpper: tickUpper, + amount0Desired: amt0, + // allow for a bit of slippage + amount0Min: amt0 - (amt0 / 1e8), + amount1Desired: amt1, + amount1Min: amt1 - (amt1 / 1e8), + deadline: block.timestamp, + recipient: address(this) + }); + + initialSqrtPrice = sqrtPrice; + } + + function _deploy( + string memory _name, + string memory _symbol, + uint256 _initialPricePerEth, + uint256 _totalSupply, + address _recipient, + uint256 _recipientAmount + ) internal returns (InstantLiquidityToken, uint256) { + // get the addresses per-chain + (address weth, INonfungiblePositionManager nonfungiblePositionManager) = getAddresses(); + address token; + { + Storage memory store = s; + // deploy and initialize a new token + token = Clones.cloneDeterministic( + address(store.instantLiquidityToken), + keccak256(abi.encode(block.chainid, store.deploymentNonce)) + ); + InstantLiquidityToken(token).initialize({ + _mintTo: address(this), + _totalSupply: _totalSupply, + _name: _name, + _symbol: _symbol + }); + s.deploymentNonce += 1; + } + + uint256 poolAmount = _totalSupply - _recipientAmount; + // approve the non-fungible position mgr for the pool liquidity amount + InstantLiquidityToken(token).approve({ + spender: address(nonfungiblePositionManager), + value: poolAmount + }); + + (INonfungiblePositionManager.MintParams memory mintParams, uint160 initialSquareRootPrice) = + _getMintParams({ + token: token, + weth: weth, + initialPricePerEth: _initialPricePerEth, + liquidityIn: poolAmount + }); + + // create the pool + nonfungiblePositionManager.createAndInitializePoolIfNecessary({ + token0: token < weth ? token : weth, + token1: token < weth ? weth : token, + fee: POOL_FEE, + sqrtPriceX96: initialSquareRootPrice + }); + + // mint the position + (uint256 lpTokenId,,,) = nonfungiblePositionManager.mint({params: mintParams}); + + // After token initialization and pool creation, transfer the recipient amount + if (_recipientAmount > 0) { + InstantLiquidityToken(token).transfer(_recipient, _recipientAmount); + } + + return (InstantLiquidityToken(token), lpTokenId); + } + + function deploy( + string memory _name, + string memory _symbol, + uint256 _initialPricePerEth, + uint256 _totalSupply, + address _recipient, + uint256 _recipientAmount + ) public returns (InstantLiquidityToken token, uint256 lpTokenId) { + if (_recipientAmount > _totalSupply) revert("Recipient amount exceeds total supply"); + + (token, lpTokenId) = + _deploy(_name, _symbol, _initialPricePerEth, _totalSupply, _recipient, _recipientAmount); + + emit TokenFactoryDeployment(address(token), lpTokenId, msg.sender, _name, _symbol); + } + + function collectFees(address _recipient, uint256[] memory _tokenIds) public onlyOwner { + (, INonfungiblePositionManager nonfungiblePositionManager) = getAddresses(); + + for (uint256 i; i < _tokenIds.length; ++i) { + nonfungiblePositionManager.collect( + INonfungiblePositionManager.CollectParams({ + recipient: _recipient, + amount0Max: type(uint128).max, + amount1Max: type(uint128).max, + tokenId: _tokenIds[i] + }) + ); + } + } + + function setInstantLiquidityToken(address _instantLiquidityToken) public onlyOwner { + s.instantLiquidityToken = InstantLiquidityToken(_instantLiquidityToken); + } +} diff --git a/src/TokenFactory.sol b/src/TokenFactory.sol index 984738f..dbeb2c3 100644 --- a/src/TokenFactory.sol +++ b/src/TokenFactory.sol @@ -162,6 +162,7 @@ contract TokenFactory is Ownable, ERC721Holder { (address token0, address token1) = tokenIsLessThanWeth ? (token, weth) : (weth, token); (int24 tickLower, int24 tickUpper) = tokenIsLessThanWeth ? (int24(-220400), int24(0)) : (int24(0), int24(220400)); + // -216600 216600 (uint256 amt0, uint256 amt1) = tokenIsLessThanWeth ? (uint256(POOL_AMOUNT), uint256(0)) : (uint256(0), uint256(POOL_AMOUNT)); @@ -184,6 +185,8 @@ contract TokenFactory is Ownable, ERC721Holder { initialSqrtPrice = tokenIsLessThanWeth ? 1252685732681638336686364 : 5010664478791732988152496286088527; + + // tokenIsLessThanWeth ? 2374716772012394972971008 : 2643305428826910585518143993544704; } function _deploy(address _recipient, string memory _name, string memory _symbol) diff --git a/src/lib/TickMath.sol b/src/lib/TickMath.sol new file mode 100644 index 0000000..b7277e9 --- /dev/null +++ b/src/lib/TickMath.sol @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +/** + * @dev imported from Uniswap v3 core + */ + +/// @title Math library for computing sqrt prices from ticks and vice versa +/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports +/// prices between 2**-128 and 2**128 +library TickMath { + /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 + int24 internal constant MIN_TICK = -887272; + /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 + int24 internal constant MAX_TICK = -MIN_TICK; + + /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) + uint160 internal constant MIN_SQRT_RATIO = 4295128739; + /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) + uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; + + /// @notice Calculates sqrt(1.0001^tick) * 2^96 + /// @dev Throws if |tick| > max tick + /// @param tick The input tick for the above formula + /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) + /// at the given tick + function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { + uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); + require(absTick <= uint256(int256(MAX_TICK)), "T"); // casted to uint256 + + uint256 ratio = absTick & 0x1 != 0 + ? 0xfffcb933bd6fad37aa2d162d1a594001 + : 0x100000000000000000000000000000000; + if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; + if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; + if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; + if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; + if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; + if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; + if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; + if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; + if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; + if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; + if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; + if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; + if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; + if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; + if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; + if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; + if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; + if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; + if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; + + if (tick > 0) ratio = type(uint256).max / ratio; + + // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. + // we then downcast because we know the result always fits within 160 bits due to our tick input constraint + // we round up in the division so getTickAtSqrtRatio of the output price is always consistent + sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); + } + + /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio + /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may + /// ever return. + /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 + /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio + function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { + // second inequality must be < because the price can never reach the price at the max tick + require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, "R"); + uint256 ratio = uint256(sqrtPriceX96) << 32; + + uint256 r = ratio; + uint256 msb = 0; + + assembly { + let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(5, gt(r, 0xFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(4, gt(r, 0xFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(3, gt(r, 0xFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(2, gt(r, 0xF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(1, gt(r, 0x3)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := gt(r, 0x1) + msb := or(msb, f) + } + + if (msb >= 128) r = ratio >> (msb - 127); + else r = ratio << (127 - msb); + + int256 log_2 = (int256(msb) - 128) << 64; + + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(63, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(62, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(61, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(60, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(59, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(58, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(57, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(56, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(55, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(54, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(53, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(52, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(51, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(50, f)) + } + + int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number + + int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); + int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); + + tick = tickLow == tickHi + ? tickLow + : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; + } +} diff --git a/src/lib/addresses.sol b/src/lib/addresses.sol new file mode 100644 index 0000000..739f23c --- /dev/null +++ b/src/lib/addresses.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {INonfungiblePositionManager} from "../TokenFactory.sol"; + +function getAddresses() + view + returns (address weth, INonfungiblePositionManager nonFungiblePositionManager) +{ + uint256 chainId = block.chainid; + // Mainnet, Goerli, Arbitrum, Optimism, Polygon + nonFungiblePositionManager = + INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88); + + // mainnet + if (chainId == 1) weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + // goerli + if (chainId == 5) weth = 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6; + // arbitrum + if (chainId == 42161) weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1; + // optimism + if (chainId == 10) weth = 0x4200000000000000000000000000000000000006; + // polygon + if (chainId == 137) weth = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; + // bnb + if (chainId == 56) { + weth = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; + nonFungiblePositionManager = + INonfungiblePositionManager(0x7b8A01B39D58278b5DE7e48c8449c9f4F5170613); + } + // base + if (chainId == 8453) { + weth = 0x4200000000000000000000000000000000000006; + nonFungiblePositionManager = + INonfungiblePositionManager(0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1); + } + // base sepolia + if (chainId == 84532) { + weth = 0x4200000000000000000000000000000000000006; + nonFungiblePositionManager = + INonfungiblePositionManager(0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2); + } + // sepolia + if (chainId == 11155111) { + weth = 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14; + nonFungiblePositionManager = + INonfungiblePositionManager(0x1238536071E1c677A632429e3655c799b22cDA52); + } + // zora + if (chainId == 7777777) { + weth = 0x4200000000000000000000000000000000000006; + nonFungiblePositionManager = + INonfungiblePositionManager(0xbC91e8DfA3fF18De43853372A3d7dfe585137D78); + } + // degen chain + if (chainId == 666666666) { + // wrapped degen + weth = 0xEb54dACB4C2ccb64F8074eceEa33b5eBb38E5387; + nonFungiblePositionManager = + // proxy swap + INonfungiblePositionManager(0x56c65e35f2Dd06f659BCFe327C4D7F21c9b69C2f); + } +} diff --git a/src/lib/priceCalc.sol b/src/lib/priceCalc.sol new file mode 100644 index 0000000..24ce2c0 --- /dev/null +++ b/src/lib/priceCalc.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol"; +import {TickMath} from "./TickMath.sol"; + +// constants for scaling and precision +uint256 constant oneEth = 10 ** 18; +uint256 constant tokenDecimals = 10 ** 18; +uint256 constant scale = 10 ** 18; +/// @dev Q96 decimals for fixed-point calculations (see Uniswap V3 docs) +uint256 constant q = 2 ** 96; + +/// @dev calculate sqrt prices and corresponding ticks for given token pair and `ethPricePerToken` price +/// @return sqrtPrice - the initial square root price for the token pair: +/// - should map 1:1 for the desired ethPricePerToken +/// - will be outside the liquidity range specified by TickLower and TickUpper +/// @return tickLower - the lower tick of the liquidity range +/// - rounded up to the nearest 100 because the fee is specified at 10_000 or 1% +/// @return tickUpper - the upper tick of the liquidity range +/// - rounded down to the nearest 100 because the fee is specified at 10_000 or 1% +function calculatePrices(address tokenA, address tokenB, uint256 ethPricePerToken) + pure + returns (uint160 sqrtPrice, int24 tickLower, int24 tickUpper) +{ + if (tokenA < tokenB) { + // scale up ethPricePerToken by 18 decimals to prevent rounding errors + uint256 ethPricePerTokenScaled = ethPricePerToken * scale; + // square root price the liquidity begins at + sqrtPrice = uint160(FixedPointMathLib.sqrt(ethPricePerTokenScaled) * q / scale); + tickLower = TickMath.getTickAtSqrtRatio(sqrtPrice) / 100 * 100; + tickLower = tickLower - (tickLower % 200); + } else { + // calculate square root of the price for tokenB per eth + uint256 oneEthScaled = oneEth * scale; + uint256 quotient = oneEthScaled / ethPricePerToken; + // square root price the liquidity begins at + sqrtPrice = uint160(FixedPointMathLib.sqrt(quotient) * q / FixedPointMathLib.sqrt(scale)); + tickUpper = TickMath.getTickAtSqrtRatio(sqrtPrice) / 100 * 100 - 100; + tickUpper = tickUpper - (tickUpper % 200); + } +} diff --git a/test/e2e.t.sol b/test/e2e.t.sol index 933e672..5e9990e 100644 --- a/test/e2e.t.sol +++ b/test/e2e.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; +import {getAddresses} from "../src/lib/addresses.sol"; import {DeployFactory} from "script/DeployFactory.s.sol"; import {DeployToken} from "script/DeployToken.s.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -13,57 +14,6 @@ import { import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -function getAddresses() - view - returns (address weth, INonfungiblePositionManager nonFungiblePositionManager) -{ - uint256 chainId = block.chainid; - // Mainnet, Goerli, Arbitrum, Optimism, Polygon - nonFungiblePositionManager = - INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88); - - // mainnet - if (chainId == 1) weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - // goerli - if (chainId == 5) weth = 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6; - // arbitrum - if (chainId == 42161) weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1; - // optimism - if (chainId == 10) weth = 0x4200000000000000000000000000000000000006; - // polygon - if (chainId == 137) weth = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; - // bnb - if (chainId == 56) { - weth = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; - nonFungiblePositionManager = - INonfungiblePositionManager(0x7b8A01B39D58278b5DE7e48c8449c9f4F5170613); - } - // base - if (chainId == 8453) { - weth = 0x4200000000000000000000000000000000000006; - nonFungiblePositionManager = - INonfungiblePositionManager(0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1); - } - // base sepolia - if (chainId == 84532) { - weth = 0x4200000000000000000000000000000000000006; - nonFungiblePositionManager = - INonfungiblePositionManager(0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2); - } - // sepolia - if (chainId == 11155111) { - weth = 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14; - nonFungiblePositionManager = - INonfungiblePositionManager(0x1238536071E1c677A632429e3655c799b22cDA52); - } - // zora - if (chainId == 7777777) { - weth = 0x4200000000000000000000000000000000000006; - nonFungiblePositionManager = - INonfungiblePositionManager(0xbC91e8DfA3fF18De43853372A3d7dfe585137D78); - } -} - contract TestEndToEndDeployment is Test { DeployFactory internal deployFactory; DeployToken internal deployToken; diff --git a/test/e2e_v2.t.sol b/test/e2e_v2.t.sol index 6aef59c..370e4c5 100644 --- a/test/e2e_v2.t.sol +++ b/test/e2e_v2.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; +import {getAddresses} from "../src/lib/addresses.sol"; import {DeployFactoryV2} from "script/DeployFactoryV2.s.sol"; import {DeployTokenV2} from "script/DeployTokenV2.s.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -14,7 +15,6 @@ import { } from "../src/TokenFactoryV2.sol"; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {getAddresses} from "./e2e.t.sol"; contract TestEndToEndDeploymentV2 is Test { DeployFactoryV2 internal deployFactoryV2; diff --git a/test/metal_funV2_e2e.t.sol b/test/metal_funV2_e2e.t.sol new file mode 100644 index 0000000..e92ca02 --- /dev/null +++ b/test/metal_funV2_e2e.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console2} from "forge-std/Test.sol"; +import {MetalFunFactoryV2, INonfungiblePositionManager} from "../src/MetalFunFactoryV2.sol"; +import {calculatePrices, TickMath} from "../src/lib/priceCalc.sol"; +import {console2} from "forge-std/console2.sol"; + +contract testMetalFunFactoryV2 is Test { + address recipient = address(0x0123); + address owner = address(0x4567); + + MetalFunFactoryV2 metalFunFactoryV2; + + function setUp() public { + metalFunFactoryV2 = new MetalFunFactoryV2(owner); + } + + function testCalculatePrices(uint256 wantPrice, uint256 totalSupply, uint256 recipientAmount) + public + { + vm.assume(wantPrice < 1 ether && wantPrice > 100); + vm.assume(totalSupply > 1 ether && totalSupply < 100_000_000_000 ether); + vm.assume(recipientAmount < totalSupply / 100); + + for (uint256 j = 0; j < 5; j++) { + metalFunFactoryV2.deploy("TestToken", "TT", wantPrice, totalSupply, recipient, recipientAmount); + } + console2.log(unicode"Pass ✅ for price: ", wantPrice); + } +} diff --git a/test/metal_fun_e2e.t.sol b/test/metal_fun_e2e.t.sol new file mode 100644 index 0000000..5090ca6 --- /dev/null +++ b/test/metal_fun_e2e.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; + +import {getAddresses} from "../src/lib/addresses.sol"; +import {DeployToken} from "script/DeployToken.s.sol"; +import {InstantLiquidityToken} from "../src/InstantLiquidityToken.sol"; +import {MetalFunFactory, INonfungiblePositionManager} from "../src/MetalFunFactory.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +contract TestMetalFunFactory is Test { + MetalFunFactory internal metalFunFactory; + address internal owner = address(0xB0b); + address internal recipient = address(0xA11c3); + address internal feeRecipient = address(0x5EeC); + address internal rando = address(0x5EeC); + + function setUp() public { + metalFunFactory = new MetalFunFactory(owner); + } + + function _test() internal { + // @spec deploy a token + vm.expectEmit({checkTopic1: false, checkTopic2: false, checkTopic3: false, checkData: true}); + emit MetalFunFactory.TokenFactoryDeployment( + address(0), 0, address(0), "InstantLiquidityToken", "ILT" + ); + + (, uint256 lpTokenId) = metalFunFactory.deploy("InstantLiquidityToken", "ILT"); + + // @spec owner should be correctly initialized + assertEq(metalFunFactory.owner(), address(owner)); + + // @spec the factory should be the owner of the LP token + (, INonfungiblePositionManager nonFungiblePositionManager) = getAddresses(); + assertEq(nonFungiblePositionManager.ownerOf(lpTokenId), address(metalFunFactory)); + + // @spec owner can call collect fees + uint256[] memory tokenIds = new uint256[](1); + tokenIds[0] = lpTokenId; + // @spec collect fees should call the nonFungiblePositionManager + vm.expectCall( + address(nonFungiblePositionManager), + abi.encodeWithSelector( + INonfungiblePositionManager.collect.selector, + INonfungiblePositionManager.CollectParams({ + tokenId: lpTokenId, + recipient: feeRecipient, + amount0Max: type(uint128).max, + amount1Max: type(uint128).max + }) + ) + ); + vm.prank(owner); + metalFunFactory.collectFees(feeRecipient, tokenIds); + // @spec the factory should still hold the lp token + assertEq(nonFungiblePositionManager.ownerOf(lpTokenId), address(metalFunFactory)); + + // @spec non owner can't collect fees + vm.prank(rando); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, rando)); + metalFunFactory.collectFees(feeRecipient, tokenIds); + } + + function test_endToEnd() public { + for (uint256 i; i < 25; i++) { + _test(); + } + + vm.prank(owner); + metalFunFactory.setInstantLiquidityToken(address(0x1234)); + + vm.prank(rando); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, rando)); + metalFunFactory.setInstantLiquidityToken(rando); + } +} diff --git a/test/mocks/UniswapV3Pool.sol b/test/mocks/UniswapV3Pool.sol new file mode 100644 index 0000000..cc72501 --- /dev/null +++ b/test/mocks/UniswapV3Pool.sol @@ -0,0 +1,3187 @@ +////// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @title FixedPoint96 +/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) +/// @dev Used in SqrtPriceMath.sol +library FixedPoint96 { + uint8 internal constant RESOLUTION = 96; + uint256 internal constant Q96 = 0x1000000000000000000000000; +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Math functions that do not check inputs or outputs +/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks +library UnsafeMath { + /// @notice Returns ceil(x / y) + /// @dev division by 0 has unspecified behavior, and must be checked externally + /// @param x The dividend + /// @param y The divisor + /// @return z The quotient, ceil(x / y) + function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + z := add(div(x, y), gt(mod(x, y), 0)) + } + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Contains 512-bit math functions +/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision +/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits +library FullMath { + /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv + function mulDiv(uint256 a, uint256 b, uint256 denominator) + internal + pure + returns (uint256 result) + { + // 512-bit multiply [prod1 prod0] = a * b + // Compute the product mod 2**256 and mod 2**256 - 1 + // then use the Chinese Remainder Theorem to reconstruct + // the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2**256 + prod0 + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(a, b, not(0)) + prod0 := mul(a, b) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division + if (prod1 == 0) { + require(denominator > 0); + assembly { + result := div(prod0, denominator) + } + return result; + } + + // Make sure the result is less than 2**256. + // Also prevents denominator == 0 + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0] + // Compute remainder using mulmod + uint256 remainder; + assembly { + remainder := mulmod(a, b, denominator) + } + // Subtract 256 bit number from 512 bit number + assembly { + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator + // Compute largest power of two divisor of denominator. + // Always >= 1. + uint256 twos = uint256(-1 * int256(denominator) & int256(denominator)); + // Divide denominator by power of two + assembly { + denominator := div(denominator, twos) + } + + // Divide [prod1 prod0] by the factors of two + assembly { + prod0 := div(prod0, twos) + } + // Shift in bits from prod1 into prod0. For this we need + // to flip `twos` such that it is 2**256 / twos. + // If twos is zero, then it becomes one + assembly { + twos := add(div(sub(0, twos), twos), 1) + } + prod0 |= prod1 * twos; + + // Invert denominator mod 2**256 + // Now that denominator is an odd number, it has an inverse + // modulo 2**256 such that denominator * inv = 1 mod 2**256. + // Compute the inverse by starting with a seed that is correct + // correct for four bits. That is, denominator * inv = 1 mod 2**4 + uint256 inv = (3 * denominator) ^ 2; + // Now use Newton-Raphson iteration to improve the precision. + // Thanks to Hensel's lifting lemma, this also works in modular + // arithmetic, doubling the correct bits in each step. + inv *= 2 - denominator * inv; // inverse mod 2**8 + inv *= 2 - denominator * inv; // inverse mod 2**16 + inv *= 2 - denominator * inv; // inverse mod 2**32 + inv *= 2 - denominator * inv; // inverse mod 2**64 + inv *= 2 - denominator * inv; // inverse mod 2**128 + inv *= 2 - denominator * inv; // inverse mod 2**256 + + // Because the division is now exact we can divide by multiplying + // with the modular inverse of denominator. This will give us the + // correct result modulo 2**256. Since the precoditions guarantee + // that the outcome is less than 2**256, this is the final result. + // We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inv; + return result; + } + + /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) + internal + pure + returns (uint256 result) + { + result = mulDiv(a, b, denominator); + if (mulmod(a, b, denominator) > 0) { + require(result < type(uint256).max); + result++; + } + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Safe casting methods +/// @notice Contains methods for safely casting between types +library SafeCast { + /// @notice Cast a uint256 to a uint160, revert on overflow + /// @param y The uint256 to be downcasted + /// @return z The downcasted integer, now type uint160 + function toUint160(uint256 y) internal pure returns (uint160 z) { + require((z = uint160(y)) == y); + } + + /// @notice Cast a int256 to a int128, revert on overflow or underflow + /// @param y The int256 to be downcasted + /// @return z The downcasted integer, now type int128 + function toInt128(int256 y) internal pure returns (int128 z) { + require((z = int128(y)) == y); + } + + /// @notice Cast a uint256 to a int256, revert on overflow + /// @param y The uint256 to be casted + /// @return z The casted integer, now type int256 + function toInt256(uint256 y) internal pure returns (int256 z) { + require(y < 2 ** 255); + z = int256(y); + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Optimized overflow and underflow safe math operations +/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost +library LowGasSafeMath { + /// @notice Returns x + y, reverts if sum overflows uint256 + /// @param x The augend + /// @param y The addend + /// @return z The sum of x and y + function add(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x + y) >= x); + } + + /// @notice Returns x - y, reverts if underflows + /// @param x The minuend + /// @param y The subtrahend + /// @return z The difference of x and y + function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x - y) <= x); + } + + /// @notice Returns x * y, reverts if overflows + /// @param x The multiplicand + /// @param y The multiplier + /// @return z The product of x and y + function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + require(x == 0 || (z = x * y) / x == y); + } + + /// @notice Returns x + y, reverts if overflows or underflows + /// @param x The augend + /// @param y The addend + /// @return z The sum of x and y + function add(int256 x, int256 y) internal pure returns (int256 z) { + require((z = x + y) >= x == (y >= 0)); + } + + /// @notice Returns x - y, reverts if overflows or underflows + /// @param x The minuend + /// @param y The subtrahend + /// @return z The difference of x and y + function sub(int256 x, int256 y) internal pure returns (int256 z) { + require((z = x - y) <= x == (y >= 0)); + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +////import './LowGasSafeMath.sol'; +////import './SafeCast.sol'; + +////import './FullMath.sol'; +////import './UnsafeMath.sol'; +////import './FixedPoint96.sol'; + +/// @title Functions based on Q64.96 sqrt price and liquidity +/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas +library SqrtPriceMath { + using LowGasSafeMath for uint256; + using SafeCast for uint256; + + /// @notice Gets the next sqrt price given a delta of token0 + /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least + /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the + /// price less in order to not send too much output. + /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), + /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount). + /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta + /// @param liquidity The amount of usable liquidity + /// @param amount How much of token0 to add or remove from virtual reserves + /// @param add Whether to add or remove the amount of token0 + /// @return The price after adding or removing amount, depending on add + function getNextSqrtPriceFromAmount0RoundingUp( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) internal pure returns (uint160) { + // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price + if (amount == 0) return sqrtPX96; + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + + if (add) { + uint256 product; + if ((product = amount * sqrtPX96) / amount == sqrtPX96) { + uint256 denominator = numerator1 + product; + if (denominator >= numerator1) { + // always fits in 160 bits + return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator)); + } + } + + return + uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))); + } else { + uint256 product; + // if the product overflows, we know the denominator underflows + // in addition, we must check that the denominator does not underflow + require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); + uint256 denominator = numerator1 - product; + return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); + } + } + + /// @notice Gets the next sqrt price given a delta of token1 + /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least + /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the + /// price less in order to not send too much output. + /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity + /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta + /// @param liquidity The amount of usable liquidity + /// @param amount How much of token1 to add, or remove, from virtual reserves + /// @param add Whether to add, or remove, the amount of token1 + /// @return The price after adding or removing `amount` + function getNextSqrtPriceFromAmount1RoundingDown( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) internal pure returns (uint160) { + // if we're adding (subtracting), rounding down requires rounding the quotient down (up) + // in both cases, avoid a mulDiv for most inputs + if (add) { + uint256 quotient = ( + amount <= type(uint160).max + ? (amount << FixedPoint96.RESOLUTION) / liquidity + : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity) + ); + + return uint256(sqrtPX96).add(quotient).toUint160(); + } else { + uint256 quotient = ( + amount <= type(uint160).max + ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity) + : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) + ); + + require(sqrtPX96 > quotient); + // always fits 160 bits + return uint160(sqrtPX96 - quotient); + } + } + + /// @notice Gets the next sqrt price given an input amount of token0 or token1 + /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds + /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount + /// @param liquidity The amount of usable liquidity + /// @param amountIn How much of token0, or token1, is being swapped in + /// @param zeroForOne Whether the amount in is token0 or token1 + /// @return sqrtQX96 The price after adding the input amount to token0 or token1 + function getNextSqrtPriceFromInput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amountIn, + bool zeroForOne + ) internal pure returns (uint160 sqrtQX96) { + require(sqrtPX96 > 0); + require(liquidity > 0); + + // round to make sure that we don't pass the target price + return zeroForOne + ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) + : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true); + } + + /// @notice Gets the next sqrt price given an output amount of token0 or token1 + /// @dev Throws if price or liquidity are 0 or the next price is out of bounds + /// @param sqrtPX96 The starting price before accounting for the output amount + /// @param liquidity The amount of usable liquidity + /// @param amountOut How much of token0, or token1, is being swapped out + /// @param zeroForOne Whether the amount out is token0 or token1 + /// @return sqrtQX96 The price after removing the output amount of token0 or token1 + function getNextSqrtPriceFromOutput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amountOut, + bool zeroForOne + ) internal pure returns (uint160 sqrtQX96) { + require(sqrtPX96 > 0); + require(liquidity > 0); + + // round to make sure that we pass the target price + return zeroForOne + ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) + : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false); + } + + /// @notice Gets the amount0 delta between two prices + /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), + /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up or down + /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices + function getAmount0Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount0) { + if (sqrtRatioAX96 > sqrtRatioBX96) { + (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + } + + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; + + require(sqrtRatioAX96 > 0); + + return roundUp + ? UnsafeMath.divRoundingUp( + FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), sqrtRatioAX96 + ) + : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; + } + + /// @notice Gets the amount1 delta between two prices + /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up, or down + /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices + function getAmount1Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount1) { + if (sqrtRatioAX96 > sqrtRatioBX96) { + (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + } + + return roundUp + ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) + : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); + } + + /// @notice Helper that gets signed token0 delta + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The change in liquidity for which to compute the amount0 delta + /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices + function getAmount0Delta(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, int128 liquidity) + internal + pure + returns (int256 amount0) + { + return liquidity < 0 + ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + } + + /// @notice Helper that gets signed token1 delta + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The change in liquidity for which to compute the amount1 delta + /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices + function getAmount1Delta(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, int128 liquidity) + internal + pure + returns (int256 amount1) + { + return liquidity < 0 + ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Minimal ERC20 interface for Uniswap +/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 +interface IERC20Minimal { + /// @notice Returns the balance of a token + /// @param account The account for which to look up the number of tokens it has, i.e. its balance + /// @return The number of tokens held by the account + function balanceOf(address account) external view returns (uint256); + + /// @notice Transfers the amount of token from the `msg.sender` to the recipient + /// @param recipient The account that will receive the amount transferred + /// @param amount The number of tokens to send from the sender to the recipient + /// @return Returns true for a successful transfer, false for an unsuccessful transfer + function transfer(address recipient, uint256 amount) external returns (bool); + + /// @notice Returns the current allowance given to a spender by an owner + /// @param owner The account of the token owner + /// @param spender The account of the token spender + /// @return The current allowance granted by `owner` to `spender` + function allowance(address owner, address spender) external view returns (uint256); + + /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` + /// @param spender The account which will be allowed to spend a given amount of the owners tokens + /// @param amount The amount of tokens allowed to be used by `spender` + /// @return Returns true for a successful approval, false for unsuccessful + function approve(address spender, uint256 amount) external returns (bool); + + /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` + /// @param sender The account from which the transfer will be initiated + /// @param recipient The recipient of the transfer + /// @param amount The amount of the transfer + /// @return Returns true for a successful transfer, false for unsuccessful + function transferFrom(address sender, address recipient, uint256 amount) + external + returns (bool); + + /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. + /// @param from The account from which the tokens were sent, i.e. the balance decreased + /// @param to The account to which the tokens were sent, i.e. the balance increased + /// @param value The amount of tokens that were transferred + event Transfer(address indexed from, address indexed to, uint256 value); + + /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. + /// @param owner The account that approved spending of its tokens + /// @param spender The account for which the spending allowance was modified + /// @param value The new allowance from the owner to the spender + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Math library for liquidity +library LiquidityMath { + /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows + /// @param x The liquidity before change + /// @param y The delta by which liquidity should be changed + /// @return z The liquidity delta + function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { + if (y < 0) { + require((z = x - uint128(-y)) < x, "LS"); + } else { + require((z = x + uint128(y)) >= x, "LA"); + } + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title FixedPoint128 +/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) +library FixedPoint128 { + uint256 internal constant Q128 = 0x100000000000000000000000000000000; +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title BitMath +/// @dev This library provides functionality for computing bit properties of an unsigned integer +library BitMath { + /// @notice Returns the index of the most significant bit of the number, + /// where the least significant bit is at index 0 and the most significant bit is at index 255 + /// @dev The function satisfies the property: + /// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) + /// @param x the value for which to compute the most significant bit, must be greater than 0 + /// @return r the index of the most significant bit + function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { + require(x > 0); + + if (x >= 0x100000000000000000000000000000000) { + x >>= 128; + r += 128; + } + if (x >= 0x10000000000000000) { + x >>= 64; + r += 64; + } + if (x >= 0x100000000) { + x >>= 32; + r += 32; + } + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 0x4) { + x >>= 2; + r += 2; + } + if (x >= 0x2) r += 1; + } + + /// @notice Returns the index of the least significant bit of the number, + /// where the least significant bit is at index 0 and the most significant bit is at index 255 + /// @dev The function satisfies the property: + /// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) + /// @param x the value for which to compute the least significant bit, must be greater than 0 + /// @return r the index of the least significant bit + function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { + require(x > 0); + + r = 255; + if (x & type(uint128).max > 0) { + r -= 128; + } else { + x >>= 128; + } + if (x & type(uint64).max > 0) { + r -= 64; + } else { + x >>= 64; + } + if (x & type(uint32).max > 0) { + r -= 32; + } else { + x >>= 32; + } + if (x & type(uint16).max > 0) { + r -= 16; + } else { + x >>= 16; + } + if (x & type(uint8).max > 0) { + r -= 8; + } else { + x >>= 8; + } + if (x & 0xf > 0) { + r -= 4; + } else { + x >>= 4; + } + if (x & 0x3 > 0) { + r -= 2; + } else { + x >>= 2; + } + if (x & 0x1 > 0) r -= 1; + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Math library for computing sqrt prices from ticks and vice versa +/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports +/// prices between 2**-128 and 2**128 +library TickMath { + /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 + int24 internal constant MIN_TICK = -887272; + /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 + int24 internal constant MAX_TICK = -MIN_TICK; + + /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) + uint160 internal constant MIN_SQRT_RATIO = 4295128739; + /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) + uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; + + /// @notice Calculates sqrt(1.0001^tick) * 2^96 + /// @dev Throws if |tick| > max tick + /// @param tick The input tick for the above formula + /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) + /// at the given tick + function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { + uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); + require(absTick <= uint256(uint24(MAX_TICK)), "T"); + + uint256 ratio = absTick & 0x1 != 0 + ? 0xfffcb933bd6fad37aa2d162d1a594001 + : 0x100000000000000000000000000000000; + if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; + if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; + if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; + if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; + if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; + if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; + if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; + if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; + if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; + if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; + if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; + if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; + if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; + if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; + if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; + if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; + if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; + if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; + if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; + + if (tick > 0) ratio = type(uint256).max / ratio; + + // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. + // we then downcast because we know the result always fits within 160 bits due to our tick input constraint + // we round up in the division so getTickAtSqrtRatio of the output price is always consistent + sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); + } + + /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio + /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may + /// ever return. + /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 + /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio + function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { + // second inequality must be < because the price can never reach the price at the max tick + require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, "R"); + uint256 ratio = uint256(sqrtPriceX96) << 32; + + uint256 r = ratio; + uint256 msb = 0; + + assembly { + let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(5, gt(r, 0xFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(4, gt(r, 0xFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(3, gt(r, 0xFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(2, gt(r, 0xF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(1, gt(r, 0x3)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := gt(r, 0x1) + msb := or(msb, f) + } + + if (msb >= 128) r = ratio >> (msb - 127); + else r = ratio << (127 - msb); + + int256 log_2 = (int256(msb) - 128) << 64; + + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(63, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(62, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(61, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(60, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(59, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(58, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(57, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(56, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(55, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(54, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(53, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(52, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(51, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(50, f)) + } + + int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number + + int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); + int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); + + tick = tickLow == tickHi + ? tickLow + : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Events emitted by a pool +/// @notice Contains all events emitted by the pool +interface IUniswapV3PoolEvents { + /// @notice Emitted exactly once by a pool when #initialize is first called on the pool + /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize + /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 + /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool + event Initialize(uint160 sqrtPriceX96, int24 tick); + + /// @notice Emitted when liquidity is minted for a given position + /// @param sender The address that minted the liquidity + /// @param owner The owner of the position and recipient of any minted liquidity + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity minted to the position range + /// @param amount0 How much token0 was required for the minted liquidity + /// @param amount1 How much token1 was required for the minted liquidity + event Mint( + address sender, + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Emitted when fees are collected by the owner of a position + /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees + /// @param owner The owner of the position for which fees are collected + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount0 The amount of token0 fees collected + /// @param amount1 The amount of token1 fees collected + event Collect( + address indexed owner, + address recipient, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount0, + uint128 amount1 + ); + + /// @notice Emitted when a position's liquidity is removed + /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect + /// @param owner The owner of the position for which liquidity is removed + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity to remove + /// @param amount0 The amount of token0 withdrawn + /// @param amount1 The amount of token1 withdrawn + event Burn( + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Emitted by the pool for any swaps between token0 and token1 + /// @param sender The address that initiated the swap call, and that received the callback + /// @param recipient The address that received the output of the swap + /// @param amount0 The delta of the token0 balance of the pool + /// @param amount1 The delta of the token1 balance of the pool + /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 + /// @param liquidity The liquidity of the pool after the swap + /// @param tick The log base 1.0001 of price of the pool after the swap + event Swap( + address indexed sender, + address indexed recipient, + int256 amount0, + int256 amount1, + uint160 sqrtPriceX96, + uint128 liquidity, + int24 tick + ); + + /// @notice Emitted by the pool for any flashes of token0/token1 + /// @param sender The address that initiated the swap call, and that received the callback + /// @param recipient The address that received the tokens from flash + /// @param amount0 The amount of token0 that was flashed + /// @param amount1 The amount of token1 that was flashed + /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee + /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee + event Flash( + address indexed sender, + address indexed recipient, + uint256 amount0, + uint256 amount1, + uint256 paid0, + uint256 paid1 + ); + + /// @notice Emitted by the pool for increases to the number of observations that can be stored + /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index + /// just before a mint/swap/burn. + /// @param observationCardinalityNextOld The previous value of the next observation cardinality + /// @param observationCardinalityNextNew The updated value of the next observation cardinality + event IncreaseObservationCardinalityNext( + uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew + ); + + /// @notice Emitted when the protocol fee is changed by the pool + /// @param feeProtocol0Old The previous value of the token0 protocol fee + /// @param feeProtocol1Old The previous value of the token1 protocol fee + /// @param feeProtocol0New The updated value of the token0 protocol fee + /// @param feeProtocol1New The updated value of the token1 protocol fee + event SetFeeProtocol( + uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New + ); + + /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner + /// @param sender The address that collects the protocol fees + /// @param recipient The address that receives the collected protocol fees + /// @param amount0 The amount of token0 protocol fees that is withdrawn + /// @param amount0 The amount of token1 protocol fees that is withdrawn + event CollectProtocol( + address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1 + ); +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Permissioned pool actions +/// @notice Contains pool methods that may only be called by the factory owner +interface IUniswapV3PoolOwnerActions { + /// @notice Set the denominator of the protocol's % share of the fees + /// @param feeProtocol0 new protocol fee for token0 of the pool + /// @param feeProtocol1 new protocol fee for token1 of the pool + function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; + + /// @notice Collect the protocol fee accrued to the pool + /// @param recipient The address to which collected protocol fees should be sent + /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 + /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 + /// @return amount0 The protocol fee collected in token0 + /// @return amount1 The protocol fee collected in token1 + function collectProtocol(address recipient, uint128 amount0Requested, uint128 amount1Requested) + external + returns (uint128 amount0, uint128 amount1); +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Permissionless pool actions +/// @notice Contains pool methods that can be called by anyone +interface IUniswapV3PoolActions { + /// @notice Sets the initial price for the pool + /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value + /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 + function initialize(uint160 sqrtPriceX96) external; + + /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position + /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback + /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends + /// on tickLower, tickUpper, the amount of liquidity, and the current price. + /// @param recipient The address for which the liquidity will be created + /// @param tickLower The lower tick of the position in which to add liquidity + /// @param tickUpper The upper tick of the position in which to add liquidity + /// @param amount The amount of liquidity to mint + /// @param data Any data that should be passed through to the callback + /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback + /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback + function mint( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount, + bytes calldata data + ) external returns (uint256 amount0, uint256 amount1); + + /// @notice Collects tokens owed to a position + /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. + /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or + /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the + /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. + /// @param recipient The address which should receive the fees collected + /// @param tickLower The lower tick of the position for which to collect fees + /// @param tickUpper The upper tick of the position for which to collect fees + /// @param amount0Requested How much token0 should be withdrawn from the fees owed + /// @param amount1Requested How much token1 should be withdrawn from the fees owed + /// @return amount0 The amount of fees collected in token0 + /// @return amount1 The amount of fees collected in token1 + function collect( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); + + /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position + /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 + /// @dev Fees must be collected separately via a call to #collect + /// @param tickLower The lower tick of the position for which to burn liquidity + /// @param tickUpper The upper tick of the position for which to burn liquidity + /// @param amount How much liquidity to burn + /// @return amount0 The amount of token0 sent to the recipient + /// @return amount1 The amount of token1 sent to the recipient + function burn(int24 tickLower, int24 tickUpper, uint128 amount) + external + returns (uint256 amount0, uint256 amount1); + + /// @notice Swap token0 for token1, or token1 for token0 + /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback + /// @param recipient The address to receive the output of the swap + /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 + /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) + /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this + /// value after the swap. If one for zero, the price cannot be greater than this value after the swap + /// @param data Any data to be passed through to the callback + /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive + /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external returns (int256 amount0, int256 amount1); + + /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback + /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback + /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling + /// with 0 amount{0,1} and sending the donation amount(s) from the callback + /// @param recipient The address which will receive the token0 and token1 amounts + /// @param amount0 The amount of token0 to send + /// @param amount1 The amount of token1 to send + /// @param data Any data to be passed through to the callback + function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) + external; + + /// @notice Increase the maximum number of price and liquidity observations that this pool will store + /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to + /// the input observationCardinalityNext. + /// @param observationCardinalityNext The desired minimum number of observations for the pool to store + function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Pool state that is not stored +/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the +/// blockchain. The functions here may have variable gas costs. +interface IUniswapV3PoolDerivedState { + /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp + /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing + /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, + /// you must call it with secondsAgos = [3600, 0]. + /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in + /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. + /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned + /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp + /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block + /// timestamp + function observe(uint32[] calldata secondsAgos) + external + view + returns ( + int56[] memory tickCumulatives, + uint160[] memory secondsPerLiquidityCumulativeX128s + ); + + /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range + /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. + /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first + /// snapshot is taken and the second snapshot is taken. + /// @param tickLower The lower tick of the range + /// @param tickUpper The upper tick of the range + /// @return tickCumulativeInside The snapshot of the tick accumulator for the range + /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range + /// @return secondsInside The snapshot of seconds per liquidity for the range + function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) + external + view + returns ( + int56 tickCumulativeInside, + uint160 secondsPerLiquidityInsideX128, + uint32 secondsInside + ); +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Pool state that can change +/// @notice These methods compose the pool's state, and can change with any frequency including multiple times +/// per transaction +interface IUniswapV3PoolState { + /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas + /// when accessed externally. + /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value + /// tick The current tick of the pool, i.e. according to the last tick transition that was run. + /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick + /// boundary. + /// observationIndex The index of the last oracle observation that was written, + /// observationCardinality The current maximum number of observations stored in the pool, + /// observationCardinalityNext The next maximum number of observations, to be updated when the observation. + /// feeProtocol The protocol fee for both tokens of the pool. + /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 + /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. + /// unlocked Whether the pool is currently locked to reentrancy + function slot0() + external + view + returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ); + + /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool + /// @dev This value can overflow the uint256 + function feeGrowthGlobal0X128() external view returns (uint256); + + /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool + /// @dev This value can overflow the uint256 + function feeGrowthGlobal1X128() external view returns (uint256); + + /// @notice The amounts of token0 and token1 that are owed to the protocol + /// @dev Protocol fees will never exceed uint128 max in either token + function protocolFees() external view returns (uint128 token0, uint128 token1); + + /// @notice The currently in range liquidity available to the pool + /// @dev This value has no relationship to the total liquidity across all ticks + function liquidity() external view returns (uint128); + + /// @notice Look up information about a specific tick in the pool + /// @param tick The tick to look up + /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or + /// tick upper, + /// liquidityNet how much liquidity changes when the pool price crosses the tick, + /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, + /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, + /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick + /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, + /// secondsOutside the seconds spent on the other side of the tick from the current tick, + /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. + /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. + /// In addition, these values are only relative and must be used only in comparison to previous snapshots for + /// a specific position. + function ticks(int24 tick) + external + view + returns ( + uint128 liquidityGross, + int128 liquidityNet, + uint256 feeGrowthOutside0X128, + uint256 feeGrowthOutside1X128, + int56 tickCumulativeOutside, + uint160 secondsPerLiquidityOutsideX128, + uint32 secondsOutside, + bool initialized + ); + + /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information + function tickBitmap(int16 wordPosition) external view returns (uint256); + + /// @notice Returns the information about a position by the position's key + /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper + /// @return _liquidity The amount of liquidity in the position, + /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, + /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, + /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, + /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke + function positions(bytes32 key) + external + view + returns ( + uint128 _liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1 + ); + + /// @notice Returns data about a specific observation index + /// @param index The element of the observations array to fetch + /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time + /// ago, rather than at a specific index in the array. + /// @return blockTimestamp The timestamp of the observation, + /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, + /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, + /// Returns initialized whether the observation has been initialized and the values are safe to use + function observations(uint256 index) + external + view + returns ( + uint32 blockTimestamp, + int56 tickCumulative, + uint160 secondsPerLiquidityCumulativeX128, + bool initialized + ); +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Pool state that never changes +/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values +interface IUniswapV3PoolImmutables { + /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface + /// @return The contract address + function factory() external view returns (address); + + /// @notice The first of the two tokens of the pool, sorted by address + /// @return The token contract address + function token0() external view returns (address); + + /// @notice The second of the two tokens of the pool, sorted by address + /// @return The token contract address + function token1() external view returns (address); + + /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 + /// @return The fee + function fee() external view returns (uint24); + + /// @notice The pool tick spacing + /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive + /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... + /// This value is an int24 to avoid casting even though it is always positive. + /// @return The tick spacing + function tickSpacing() external view returns (int24); + + /// @notice The maximum amount of position liquidity that can use any tick in the range + /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and + /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool + /// @return The max amount of liquidity per tick + function maxLiquidityPerTick() external view returns (uint128); +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Callback for IUniswapV3PoolActions#flash +/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface +interface IUniswapV3FlashCallback { + /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash. + /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// @param fee0 The fee amount in token0 due to the pool by the end of the flash + /// @param fee1 The fee amount in token1 due to the pool by the end of the flash + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call + function uniswapV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external; +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Callback for IUniswapV3PoolActions#swap +/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface +interface IUniswapV3SwapCallback { + /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. + /// @dev In the implementation you must pay the pool tokens owed for the swap. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. + /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. + /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call + function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) + external; +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Callback for IUniswapV3PoolActions#mint +/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface +interface IUniswapV3MintCallback { + /// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint. + /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity + /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call + function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata data) + external; +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title The interface for the Uniswap V3 Factory +/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees +interface IUniswapV3Factory { + /// @notice Emitted when the owner of the factory is changed + /// @param oldOwner The owner before the owner was changed + /// @param newOwner The owner after the owner was changed + event OwnerChanged(address indexed oldOwner, address indexed newOwner); + + /// @notice Emitted when a pool is created + /// @param token0 The first token of the pool by address sort order + /// @param token1 The second token of the pool by address sort order + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks + /// @param pool The address of the created pool + event PoolCreated( + address indexed token0, + address indexed token1, + uint24 indexed fee, + int24 tickSpacing, + address pool + ); + + /// @notice Emitted when a new fee amount is enabled for pool creation via the factory + /// @param fee The enabled fee, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee + event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); + + /// @notice Returns the current owner of the factory + /// @dev Can be changed by the current owner via setOwner + /// @return The address of the factory owner + function owner() external view returns (address); + + /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled + /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context + /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee + /// @return The tick spacing + function feeAmountTickSpacing(uint24 fee) external view returns (int24); + + /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist + /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order + /// @param tokenA The contract address of either token0 or token1 + /// @param tokenB The contract address of the other token + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @return pool The pool address + function getPool(address tokenA, address tokenB, uint24 fee) + external + view + returns (address pool); + + /// @notice Creates a pool for the given two tokens and fee + /// @param tokenA One of the two tokens in the desired pool + /// @param tokenB The other of the two tokens in the desired pool + /// @param fee The desired fee for the pool + /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved + /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments + /// are invalid. + /// @return pool The address of the newly created pool + function createPool(address tokenA, address tokenB, uint24 fee) + external + returns (address pool); + + /// @notice Updates the owner of the factory + /// @dev Must be called by the current owner + /// @param _owner The new owner of the factory + function setOwner(address _owner) external; + + /// @notice Enables a fee amount with the given tickSpacing + /// @dev Fee amounts may never be removed once enabled + /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) + /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount + function enableFeeAmount(uint24 fee, int24 tickSpacing) external; +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools +/// @notice A contract that constructs a pool must implement this to pass arguments to the pool +/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash +/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain +interface IUniswapV3PoolDeployer { + /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation. + /// @dev Called by the pool constructor to fetch the parameters of the pool + /// Returns factory The factory address + /// Returns token0 The first token of the pool by address sort order + /// Returns token1 The second token of the pool by address sort order + /// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// Returns tickSpacing The minimum number of ticks between initialized ticks + function parameters() + external + view + returns (address factory, address token0, address token1, uint24 fee, int24 tickSpacing); +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +////import './FullMath.sol'; +////import './SqrtPriceMath.sol'; + +/// @title Computes the result of a swap within ticks +/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. +library SwapMath { + /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap + /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive + /// @param sqrtRatioCurrentX96 The current sqrt price of the pool + /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred + /// @param liquidity The usable liquidity + /// @param amountRemaining How much input or output amount is remaining to be swapped in/out + /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip + /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target + /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap + /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap + /// @return feeAmount The amount of input that will be taken as a fee + function computeSwapStep( + uint160 sqrtRatioCurrentX96, + uint160 sqrtRatioTargetX96, + uint128 liquidity, + int256 amountRemaining, + uint24 feePips + ) + internal + pure + returns (uint160 sqrtRatioNextX96, uint256 amountIn, uint256 amountOut, uint256 feeAmount) + { + bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; + bool exactIn = amountRemaining >= 0; + + if (exactIn) { + uint256 amountRemainingLessFee = + FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6); + amountIn = zeroForOne + ? SqrtPriceMath.getAmount0Delta( + sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true + ) + : SqrtPriceMath.getAmount1Delta( + sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true + ); + if (amountRemainingLessFee >= amountIn) { + sqrtRatioNextX96 = sqrtRatioTargetX96; + } else { + sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput( + sqrtRatioCurrentX96, liquidity, amountRemainingLessFee, zeroForOne + ); + } + } else { + amountOut = zeroForOne + ? SqrtPriceMath.getAmount1Delta( + sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false + ) + : SqrtPriceMath.getAmount0Delta( + sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false + ); + if (uint256(-amountRemaining) >= amountOut) { + sqrtRatioNextX96 = sqrtRatioTargetX96; + } else { + sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( + sqrtRatioCurrentX96, liquidity, uint256(-amountRemaining), zeroForOne + ); + } + } + + bool max = sqrtRatioTargetX96 == sqrtRatioNextX96; + + // get the input/output amounts + if (zeroForOne) { + amountIn = max && exactIn + ? amountIn + : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true); + amountOut = max && !exactIn + ? amountOut + : SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false); + } else { + amountIn = max && exactIn + ? amountIn + : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true); + amountOut = max && !exactIn + ? amountOut + : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false); + } + + // cap the output amount to not exceed the remaining output amount + if (!exactIn && amountOut > uint256(-amountRemaining)) { + amountOut = uint256(-amountRemaining); + } + + if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) { + // we didn't reach the target, so take the remainder of the maximum input as fee + feeAmount = uint256(amountRemaining) - amountIn; + } else { + feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); + } + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +////import '../interfaces/IERC20Minimal.sol'; + +/// @title TransferHelper +/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false +library TransferHelper { + /// @notice Transfers tokens from msg.sender to a recipient + /// @dev Calls transfer on token contract, errors with TF if transfer fails + /// @param token The contract address of the token which will be transferred + /// @param to The recipient of the transfer + /// @param value The value of the transfer + function safeTransfer(address token, address to, uint256 value) internal { + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), "TF"); + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Oracle +/// @notice Provides price and liquidity data useful for a wide variety of system designs +/// @dev Instances of stored oracle data, "observations", are collected in the oracle array +/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the +/// maximum length of the oracle array. New slots will be added when the array is fully populated. +/// Observations are overwritten when the full length of the oracle array is populated. +/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe() +library Oracle { + struct Observation { + // the block timestamp of the observation + uint32 blockTimestamp; + // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized + int56 tickCumulative; + // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized + uint160 secondsPerLiquidityCumulativeX128; + // whether or not the observation is initialized + bool initialized; + } + + /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values + /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows + /// @param last The specified observation to be transformed + /// @param blockTimestamp The timestamp of the new observation + /// @param tick The active tick at the time of the new observation + /// @param liquidity The total in-range liquidity at the time of the new observation + /// @return Observation The newly populated observation + function transform( + Observation memory last, + uint32 blockTimestamp, + int24 tick, + uint128 liquidity + ) private pure returns (Observation memory) { + // uint32 delta = blockTimestamp - last.blockTimestamp; + // return Observation({ + // blockTimestamp: blockTimestamp, + // tickCumulative: last.tickCumulative + int56(tick) * int56(uint56(delta)), + // secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 + // + ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)), + // initialized: true + // }); + } + + /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array + /// @param self The stored oracle array + /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32 + /// @return cardinality The number of populated elements in the oracle array + /// @return cardinalityNext The new length of the oracle array, independent of population + function initialize(Observation[65535] storage self, uint32 time) + internal + returns (uint16 cardinality, uint16 cardinalityNext) + { + self[0] = Observation({ + blockTimestamp: time, + tickCumulative: 0, + secondsPerLiquidityCumulativeX128: 0, + initialized: true + }); + return (1, 1); + } + + /// @notice Writes an oracle observation to the array + /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally. + /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality + /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering. + /// @param self The stored oracle array + /// @param index The index of the observation that was most recently written to the observations array + /// @param blockTimestamp The timestamp of the new observation + /// @param tick The active tick at the time of the new observation + /// @param liquidity The total in-range liquidity at the time of the new observation + /// @param cardinality The number of populated elements in the oracle array + /// @param cardinalityNext The new length of the oracle array, independent of population + /// @return indexUpdated The new index of the most recently written element in the oracle array + /// @return cardinalityUpdated The new cardinality of the oracle array + function write( + Observation[65535] storage self, + uint16 index, + uint32 blockTimestamp, + int24 tick, + uint128 liquidity, + uint16 cardinality, + uint16 cardinalityNext + ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) { + Observation memory last = self[index]; + + // early return if we've already written an observation this block + if (last.blockTimestamp == blockTimestamp) return (index, cardinality); + + // if the conditions are right, we can bump the cardinality + if (cardinalityNext > cardinality && index == (cardinality - 1)) { + cardinalityUpdated = cardinalityNext; + } else { + cardinalityUpdated = cardinality; + } + + indexUpdated = (index + 1) % cardinalityUpdated; + self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity); + } + + /// @notice Prepares the oracle array to store up to `next` observations + /// @param self The stored oracle array + /// @param current The current next cardinality of the oracle array + /// @param next The proposed next cardinality which will be populated in the oracle array + /// @return next The next cardinality which will be populated in the oracle array + function grow(Observation[65535] storage self, uint16 current, uint16 next) + internal + returns (uint16) + { + require(current > 0, "I"); + // no-op if the passed next value isn't greater than the current next value + if (next <= current) return current; + // store in each slot to prevent fresh SSTOREs in swaps + // this data will not be used because the initialized boolean is still false + for (uint16 i = current; i < next; i++) { + self[i].blockTimestamp = 1; + } + return next; + } + + /// @notice comparator for 32-bit timestamps + /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time + /// @param time A timestamp truncated to 32 bits + /// @param a A comparison timestamp from which to determine the relative position of `time` + /// @param b From which to determine the relative position of `time` + /// @return bool Whether `a` is chronologically <= `b` + function lte(uint32 time, uint32 a, uint32 b) private pure returns (bool) { + // if there hasn't been overflow, no need to adjust + if (a <= time && b <= time) return a <= b; + + uint256 aAdjusted = a > time ? a : a + 2 ** 32; + uint256 bAdjusted = b > time ? b : b + 2 ** 32; + + return aAdjusted <= bAdjusted; + } + + /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. + /// The result may be the same observation, or adjacent observations. + /// @dev The answer must be contained in the array, used when the target is located within the stored observation + /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param target The timestamp at which the reserved observation should be for + /// @param index The index of the observation that was most recently written to the observations array + /// @param cardinality The number of populated elements in the oracle array + /// @return beforeOrAt The observation recorded before, or at, the target + /// @return atOrAfter The observation recorded at, or after, the target + function binarySearch( + Observation[65535] storage self, + uint32 time, + uint32 target, + uint16 index, + uint16 cardinality + ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { + uint256 l = (index + 1) % cardinality; // oldest observation + uint256 r = l + cardinality - 1; // newest observation + uint256 i; + while (true) { + i = (l + r) / 2; + + beforeOrAt = self[i % cardinality]; + + // we've landed on an uninitialized tick, keep searching higher (more recently) + if (!beforeOrAt.initialized) { + l = i + 1; + continue; + } + + atOrAfter = self[(i + 1) % cardinality]; + + bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); + + // check if we've found the answer! + if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; + + if (!targetAtOrAfter) r = i - 1; + else l = i + 1; + } + } + + /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied + /// @dev Assumes there is at least 1 initialized observation. + /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp. + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param target The timestamp at which the reserved observation should be for + /// @param tick The active tick at the time of the returned or simulated observation + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The total pool liquidity at the time of the call + /// @param cardinality The number of populated elements in the oracle array + /// @return beforeOrAt The observation which occurred at, or before, the given timestamp + /// @return atOrAfter The observation which occurred at, or after, the given timestamp + function getSurroundingObservations( + Observation[65535] storage self, + uint32 time, + uint32 target, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { + // optimistically set before to the newest observation + beforeOrAt = self[index]; + + // if the target is chronologically at or after the newest observation, we can early return + if (lte(time, beforeOrAt.blockTimestamp, target)) { + if (beforeOrAt.blockTimestamp == target) { + // if newest observation equals target, we're in the same block, so we can ignore atOrAfter + return (beforeOrAt, atOrAfter); + } else { + // otherwise, we need to transform + return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity)); + } + } + + // now, set before to the oldest observation + beforeOrAt = self[(index + 1) % cardinality]; + if (!beforeOrAt.initialized) beforeOrAt = self[0]; + + // ensure that the target is chronologically at or after the oldest observation + require(lte(time, beforeOrAt.blockTimestamp, target), "OLD"); + + // if we've reached this point, we have to binary search + return binarySearch(self, time, target, index, cardinality); + } + + /// @dev Reverts if an observation at or before the desired observation timestamp does not exist. + /// 0 may be passed as `secondsAgo' to return the current cumulative values. + /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values + /// at exactly the timestamp between the two observations. + /// @param self The stored oracle array + /// @param time The current block timestamp + /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation + /// @param tick The current tick + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The current in-range pool liquidity + /// @param cardinality The number of populated elements in the oracle array + /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo` + /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo` + function observeSingle( + Observation[65535] storage self, + uint32 time, + uint32 secondsAgo, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) { + // if (secondsAgo == 0) { + // Observation memory last = self[index]; + // if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity); + // return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128); + // } + + // uint32 target = time - secondsAgo; + + // (Observation memory beforeOrAt, Observation memory atOrAfter) = + // getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality); + + // if (target == beforeOrAt.blockTimestamp) { + // // we're at the left boundary + // return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128); + // } else if (target == atOrAfter.blockTimestamp) { + // // we're at the right boundary + // return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128); + // } else { + // // we're in the middle + // uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp; + // uint32 targetDelta = target - beforeOrAt.blockTimestamp; + // return ( + // beforeOrAt.tickCumulative + // + ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) + // * targetDelta, + // beforeOrAt.secondsPerLiquidityCumulativeX128 + // + uint160( + // ( + // uint256( + // atOrAfter.secondsPerLiquidityCumulativeX128 + // - beforeOrAt.secondsPerLiquidityCumulativeX128 + // ) * targetDelta + // ) / observationTimeDelta + // ) + // ); + // } + } + + /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos` + /// @dev Reverts if `secondsAgos` > oldest observation + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation + /// @param tick The current tick + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The current in-range pool liquidity + /// @param cardinality The number of populated elements in the oracle array + /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo` + /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo` + function observe( + Observation[65535] storage self, + uint32 time, + uint32[] memory secondsAgos, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) + internal + view + returns ( + int56[] memory tickCumulatives, + uint160[] memory secondsPerLiquidityCumulativeX128s + ) + { + require(cardinality > 0, "I"); + + tickCumulatives = new int56[](secondsAgos.length); + secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length); + for (uint256 i = 0; i < secondsAgos.length; i++) { + (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = + observeSingle(self, time, secondsAgos[i], tick, index, liquidity, cardinality); + } + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +////import './FullMath.sol'; +////import './FixedPoint128.sol'; +////import './LiquidityMath.sol'; + +/// @title Position +/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary +/// @dev Positions store additional state for tracking fees owed to the position +library Position { + // info stored for each user's position + struct Info { + // the amount of liquidity owned by this position + uint128 liquidity; + // fee growth per unit of liquidity as of the last update to liquidity or fees owed + uint256 feeGrowthInside0LastX128; + uint256 feeGrowthInside1LastX128; + // the fees owed to the position owner in token0/token1 + uint128 tokensOwed0; + uint128 tokensOwed1; + } + + /// @notice Returns the Info struct of a position, given an owner and position boundaries + /// @param self The mapping containing all user positions + /// @param owner The address of the position owner + /// @param tickLower The lower tick boundary of the position + /// @param tickUpper The upper tick boundary of the position + /// @return position The position info struct of the given owners' position + function get( + mapping(bytes32 => Info) storage self, + address owner, + int24 tickLower, + int24 tickUpper + ) internal view returns (Position.Info storage position) { + position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))]; + } + + /// @notice Credits accumulated fees to a user's position + /// @param self The individual position to update + /// @param liquidityDelta The change in pool liquidity as a result of the position update + /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries + /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries + function update( + Info storage self, + int128 liquidityDelta, + uint256 feeGrowthInside0X128, + uint256 feeGrowthInside1X128 + ) internal { + Info memory _self = self; + + uint128 liquidityNext; + if (liquidityDelta == 0) { + require(_self.liquidity > 0, "NP"); // disallow pokes for 0 liquidity positions + liquidityNext = _self.liquidity; + } else { + liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta); + } + + // calculate accumulated fees + uint128 tokensOwed0 = uint128( + FullMath.mulDiv( + feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, + _self.liquidity, + FixedPoint128.Q128 + ) + ); + uint128 tokensOwed1 = uint128( + FullMath.mulDiv( + feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, + _self.liquidity, + FixedPoint128.Q128 + ) + ); + + // update the position + if (liquidityDelta != 0) self.liquidity = liquidityNext; + self.feeGrowthInside0LastX128 = feeGrowthInside0X128; + self.feeGrowthInside1LastX128 = feeGrowthInside1X128; + if (tokensOwed0 > 0 || tokensOwed1 > 0) { + // overflow is acceptable, have to withdraw before you hit type(uint128).max fees + self.tokensOwed0 += tokensOwed0; + self.tokensOwed1 += tokensOwed1; + } + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +////import './BitMath.sol'; + +/// @title Packed tick initialized state library +/// @notice Stores a packed mapping of tick index to its initialized state +/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word. +library TickBitmap { + /// @notice Computes the position in the mapping where the initialized bit for a tick lives + /// @param tick The tick for which to compute the position + /// @return wordPos The key in the mapping containing the word in which the bit is stored + /// @return bitPos The bit position in the word where the flag is stored + function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) { + wordPos = int16(tick >> 8); + bitPos = uint8(uint24(tick % 256)); + } + + /// @notice Flips the initialized state for a given tick from false to true, or vice versa + /// @param self The mapping in which to flip the tick + /// @param tick The tick to flip + /// @param tickSpacing The spacing between usable ticks + function flipTick(mapping(int16 => uint256) storage self, int24 tick, int24 tickSpacing) + internal + { + require(tick % tickSpacing == 0); // ensure that the tick is spaced + (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing); + uint256 mask = 1 << bitPos; + self[wordPos] ^= mask; + } + + /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either + /// to the left (less than or equal to) or right (greater than) of the given tick + /// @param self The mapping in which to compute the next initialized tick + /// @param tick The starting tick + /// @param tickSpacing The spacing between usable ticks + /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick) + /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick + /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks + function nextInitializedTickWithinOneWord( + mapping(int16 => uint256) storage self, + int24 tick, + int24 tickSpacing, + bool lte + ) internal view returns (int24 next, bool initialized) { + int24 compressed = tick / tickSpacing; + if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity + + if (lte) { + (int16 wordPos, uint8 bitPos) = position(compressed); + // all the 1s at or to the right of the current bitPos + uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); + uint256 masked = self[wordPos] & mask; + + // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word + initialized = masked != 0; + // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick + next = initialized + ? (compressed - int24(uint24(bitPos - uint8(BitMath.mostSignificantBit(masked))))) + * tickSpacing + : (compressed - int24(uint24(bitPos))) * tickSpacing; + } else { + // start from the word of the next tick, since the current tick state doesn't matter + (int16 wordPos, uint8 bitPos) = position(compressed + 1); + // all the 1s at or to the left of the bitPos + uint256 mask = ~((1 << bitPos) - 1); + uint256 masked = self[wordPos] & mask; + + // if there are no initialized ticks to the left of the current tick, return leftmost in the word + initialized = masked != 0; + // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick + next = initialized + ? (compressed + 1 + int24(uint24(BitMath.leastSignificantBit(masked) - bitPos))) + * tickSpacing + : (compressed + 1 + int24(uint24(type(uint8).max - bitPos))) * tickSpacing; + } + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +////import './LowGasSafeMath.sol'; +////import './SafeCast.sol'; + +////import './TickMath.sol'; +////import './LiquidityMath.sol'; + +/// @title Tick +/// @notice Contains functions for managing tick processes and relevant calculations +library Tick { + using LowGasSafeMath for int256; + using SafeCast for int256; + + // info stored for each initialized individual tick + struct Info { + // the total position liquidity that references this tick + uint128 liquidityGross; + // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left), + int128 liquidityNet; + // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint256 feeGrowthOutside0X128; + uint256 feeGrowthOutside1X128; + // the cumulative tick value on the other side of the tick + int56 tickCumulativeOutside; + // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint160 secondsPerLiquidityOutsideX128; + // the seconds spent on the other side of the tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint32 secondsOutside; + // true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0 + // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks + bool initialized; + } + + /// @notice Derives max liquidity per tick from given tick spacing + /// @dev Executed within the pool constructor + /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing` + /// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ... + /// @return The max liquidity per tick + function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) { + int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; + int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; + uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1; + return type(uint128).max / numTicks; + } + + /// @notice Retrieves fee growth data + /// @param self The mapping containing all tick information for initialized ticks + /// @param tickLower The lower tick boundary of the position + /// @param tickUpper The upper tick boundary of the position + /// @param tickCurrent The current tick + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries + /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries + function getFeeGrowthInside( + mapping(int24 => Tick.Info) storage self, + int24 tickLower, + int24 tickUpper, + int24 tickCurrent, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128 + ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { + Info storage lower = self[tickLower]; + Info storage upper = self[tickUpper]; + + // calculate fee growth below + uint256 feeGrowthBelow0X128; + uint256 feeGrowthBelow1X128; + if (tickCurrent >= tickLower) { + feeGrowthBelow0X128 = lower.feeGrowthOutside0X128; + feeGrowthBelow1X128 = lower.feeGrowthOutside1X128; + } else { + feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128; + feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128; + } + + // calculate fee growth above + uint256 feeGrowthAbove0X128; + uint256 feeGrowthAbove1X128; + if (tickCurrent < tickUpper) { + feeGrowthAbove0X128 = upper.feeGrowthOutside0X128; + feeGrowthAbove1X128 = upper.feeGrowthOutside1X128; + } else { + feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128; + feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128; + } + + feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128; + feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128; + } + + /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa + /// @param self The mapping containing all tick information for initialized ticks + /// @param tick The tick that will be updated + /// @param tickCurrent The current tick + /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left) + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool + /// @param tickCumulative The tick * time elapsed since the pool was first initialized + /// @param time The current block timestamp cast to a uint32 + /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick + /// @param maxLiquidity The maximum liquidity allocation for a single tick + /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa + function update( + mapping(int24 => Tick.Info) storage self, + int24 tick, + int24 tickCurrent, + int128 liquidityDelta, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time, + bool upper, + uint128 maxLiquidity + ) internal returns (bool flipped) { + Tick.Info storage info = self[tick]; + + uint128 liquidityGrossBefore = info.liquidityGross; + uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); + + require(liquidityGrossAfter <= maxLiquidity, "LO"); + + flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); + + if (liquidityGrossBefore == 0) { + // by convention, we assume that all growth before a tick was initialized happened _below_ the tick + if (tick <= tickCurrent) { + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128; + info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128; + info.tickCumulativeOutside = tickCumulative; + info.secondsOutside = time; + } + info.initialized = true; + } + + info.liquidityGross = liquidityGrossAfter; + + // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) + info.liquidityNet = upper + ? int256(info.liquidityNet).sub(liquidityDelta).toInt128() + : int256(info.liquidityNet).add(liquidityDelta).toInt128(); + } + + /// @notice Clears tick data + /// @param self The mapping containing all initialized tick information for initialized ticks + /// @param tick The tick that will be cleared + function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal { + delete self[tick]; + } + + /// @notice Transitions to next tick as needed by price movement + /// @param self The mapping containing all tick information for initialized ticks + /// @param tick The destination tick of the transition + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity + /// @param tickCumulative The tick * time elapsed since the pool was first initialized + /// @param time The current block.timestamp + /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) + function cross( + mapping(int24 => Tick.Info) storage self, + int24 tick, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time + ) internal returns (int128 liquidityNet) { + Tick.Info storage info = self[tick]; + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128; + info.secondsPerLiquidityOutsideX128 = + secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128; + info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside; + info.secondsOutside = time - info.secondsOutside; + liquidityNet = info.liquidityNet; + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +/// @title Prevents delegatecall to a contract +/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract +abstract contract NoDelegateCall { + /// @dev The original address of this contract + address private original; + + constructor() { + // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode. + // In other words, this variable won't change when it's checked at runtime. + original = address(this); + } + + /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method, + /// and the use of immutable means the address bytes are copied in every place the modifier is used. + function checkNotDelegateCall() private view { + require(address(this) == original); + } + + /// @notice Prevents delegatecall into the modified method + modifier noDelegateCall() { + checkNotDelegateCall(); + _; + } +} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +////import './pool/IUniswapV3PoolImmutables.sol'; +////import './pool/IUniswapV3PoolState.sol'; +////import './pool/IUniswapV3PoolDerivedState.sol'; +////import './pool/IUniswapV3PoolActions.sol'; +////import './pool/IUniswapV3PoolOwnerActions.sol'; +////import './pool/IUniswapV3PoolEvents.sol'; + +/// @title The interface for a Uniswap V3 Pool +/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform +/// to the ERC20 specification +/// @dev The pool interface is broken up into many smaller pieces +interface IUniswapV3Pool is + IUniswapV3PoolImmutables, + IUniswapV3PoolState, + IUniswapV3PoolDerivedState, + IUniswapV3PoolActions, + IUniswapV3PoolOwnerActions, + IUniswapV3PoolEvents +{} + +/** + * SourceUnit: /Users/colinnielsen/code/univ3/v3-core/contracts/UniswapV3Pool.sol + */ + +////import './interfaces/IUniswapV3Pool.sol'; + +////import './NoDelegateCall.sol'; + +////import './libraries/LowGasSafeMath.sol'; +////import './libraries/SafeCast.sol'; +////import './libraries/Tick.sol'; +////import './libraries/TickBitmap.sol'; +////import './libraries/Position.sol'; +////import './libraries/Oracle.sol'; + +////import './libraries/FullMath.sol'; +////import './libraries/FixedPoint128.sol'; +////import './libraries/TransferHelper.sol'; +////import './libraries/TickMath.sol'; +////import './libraries/LiquidityMath.sol'; +////import './libraries/SqrtPriceMath.sol'; +////import './libraries/SwapMath.sol'; + +////import './interfaces/IUniswapV3PoolDeployer.sol'; +////import './interfaces/IUniswapV3Factory.sol'; +////import './interfaces/IERC20Minimal.sol'; +////import './interfaces/callback/IUniswapV3MintCallback.sol'; +////import './interfaces/callback/IUniswapV3SwapCallback.sol'; +////import './interfaces/callback/IUniswapV3FlashCallback.sol'; + +contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { + using LowGasSafeMath for uint256; + using LowGasSafeMath for int256; + using SafeCast for uint256; + using SafeCast for int256; + using Tick for mapping(int24 => Tick.Info); + using TickBitmap for mapping(int16 => uint256); + using Position for mapping(bytes32 => Position.Info); + using Position for Position.Info; + using Oracle for Oracle.Observation[65535]; + + /// @inheritdoc IUniswapV3PoolImmutables + address public override factory; + /// @inheritdoc IUniswapV3PoolImmutables + address public override token0; + /// @inheritdoc IUniswapV3PoolImmutables + address public override token1; + /// @inheritdoc IUniswapV3PoolImmutables + uint24 public override fee; + + /// @inheritdoc IUniswapV3PoolImmutables + int24 public override tickSpacing; + + /// @inheritdoc IUniswapV3PoolImmutables + uint128 public override maxLiquidityPerTick; + + struct Slot0 { + // the current price + uint160 sqrtPriceX96; + // the current tick + int24 tick; + // the most-recently updated index of the observations array + uint16 observationIndex; + // the current maximum number of observations that are being stored + uint16 observationCardinality; + // the next maximum number of observations to store, triggered in observations.write + uint16 observationCardinalityNext; + // the current protocol fee as a percentage of the swap fee taken on withdrawal + // represented as an integer denominator (1/x)% + uint8 feeProtocol; + // whether the pool is locked + bool unlocked; + } + /// @inheritdoc IUniswapV3PoolState + + Slot0 public override slot0; + + /// @inheritdoc IUniswapV3PoolState + uint256 public override feeGrowthGlobal0X128; + /// @inheritdoc IUniswapV3PoolState + uint256 public override feeGrowthGlobal1X128; + + // accumulated protocol fees in token0/token1 units + struct ProtocolFees { + uint128 token0; + uint128 token1; + } + /// @inheritdoc IUniswapV3PoolState + + ProtocolFees public override protocolFees; + + /// @inheritdoc IUniswapV3PoolState + uint128 public override liquidity; + + /// @inheritdoc IUniswapV3PoolState + mapping(int24 => Tick.Info) public override ticks; + /// @inheritdoc IUniswapV3PoolState + mapping(int16 => uint256) public override tickBitmap; + /// @inheritdoc IUniswapV3PoolState + mapping(bytes32 => Position.Info) public override positions; + /// @inheritdoc IUniswapV3PoolState + Oracle.Observation[65535] public override observations; + + /// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance + /// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because + /// we use balance checks to determine the payment status of interactions such as mint, swap and flash. + modifier lock() { + require(slot0.unlocked, "LOK"); + slot0.unlocked = false; + _; + slot0.unlocked = true; + } + + /// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner() + modifier onlyFactoryOwner() { + require(msg.sender == IUniswapV3Factory(factory).owner()); + _; + } + + constructor() { + int24 _tickSpacing; + (factory, token0, token1, fee, _tickSpacing) = + IUniswapV3PoolDeployer(msg.sender).parameters(); + tickSpacing = _tickSpacing; + + maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing); + } + + /// @dev Common checks for valid tick inputs. + function checkTicks(int24 tickLower, int24 tickUpper) private pure { + require(tickLower < tickUpper, "TLU"); + require(tickLower >= TickMath.MIN_TICK, "TLM"); + require(tickUpper <= TickMath.MAX_TICK, "TUM"); + } + + /// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests. + function _blockTimestamp() internal view virtual returns (uint32) { + return uint32(block.timestamp); // truncation is desired + } + + /// @dev Get the pool's balance of token0 + /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize + /// check + function balance0() private view returns (uint256) { + (bool success, bytes memory data) = token0.staticcall( + abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) + ); + require(success && data.length >= 32); + return abi.decode(data, (uint256)); + } + + /// @dev Get the pool's balance of token1 + /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize + /// check + function balance1() private view returns (uint256) { + (bool success, bytes memory data) = token1.staticcall( + abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) + ); + require(success && data.length >= 32); + return abi.decode(data, (uint256)); + } + + /// @inheritdoc IUniswapV3PoolDerivedState + function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) + external + view + override + noDelegateCall + returns ( + int56 tickCumulativeInside, + uint160 secondsPerLiquidityInsideX128, + uint32 secondsInside + ) + { + checkTicks(tickLower, tickUpper); + + int56 tickCumulativeLower; + int56 tickCumulativeUpper; + uint160 secondsPerLiquidityOutsideLowerX128; + uint160 secondsPerLiquidityOutsideUpperX128; + uint32 secondsOutsideLower; + uint32 secondsOutsideUpper; + + { + Tick.Info storage lower = ticks[tickLower]; + Tick.Info storage upper = ticks[tickUpper]; + bool initializedLower; + ( + tickCumulativeLower, + secondsPerLiquidityOutsideLowerX128, + secondsOutsideLower, + initializedLower + ) = ( + lower.tickCumulativeOutside, + lower.secondsPerLiquidityOutsideX128, + lower.secondsOutside, + lower.initialized + ); + require(initializedLower); + + bool initializedUpper; + ( + tickCumulativeUpper, + secondsPerLiquidityOutsideUpperX128, + secondsOutsideUpper, + initializedUpper + ) = ( + upper.tickCumulativeOutside, + upper.secondsPerLiquidityOutsideX128, + upper.secondsOutside, + upper.initialized + ); + require(initializedUpper); + } + + Slot0 memory _slot0 = slot0; + + if (_slot0.tick < tickLower) { + return ( + tickCumulativeLower - tickCumulativeUpper, + secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128, + secondsOutsideLower - secondsOutsideUpper + ); + } else if (_slot0.tick < tickUpper) { + uint32 time = _blockTimestamp(); + (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations + .observeSingle( + time, + 0, + _slot0.tick, + _slot0.observationIndex, + liquidity, + _slot0.observationCardinality + ); + return ( + tickCumulative - tickCumulativeLower - tickCumulativeUpper, + secondsPerLiquidityCumulativeX128 - secondsPerLiquidityOutsideLowerX128 + - secondsPerLiquidityOutsideUpperX128, + time - secondsOutsideLower - secondsOutsideUpper + ); + } else { + return ( + tickCumulativeUpper - tickCumulativeLower, + secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128, + secondsOutsideUpper - secondsOutsideLower + ); + } + } + + /// @inheritdoc IUniswapV3PoolDerivedState + function observe(uint32[] calldata secondsAgos) + external + view + override + noDelegateCall + returns ( + int56[] memory tickCumulatives, + uint160[] memory secondsPerLiquidityCumulativeX128s + ) + { + return observations.observe( + _blockTimestamp(), + secondsAgos, + slot0.tick, + slot0.observationIndex, + liquidity, + slot0.observationCardinality + ); + } + + /// @inheritdoc IUniswapV3PoolActions + function increaseObservationCardinalityNext(uint16 observationCardinalityNext) + external + override + lock + noDelegateCall + { + uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event + uint16 observationCardinalityNextNew = + observations.grow(observationCardinalityNextOld, observationCardinalityNext); + slot0.observationCardinalityNext = observationCardinalityNextNew; + if (observationCardinalityNextOld != observationCardinalityNextNew) { + emit IncreaseObservationCardinalityNext( + observationCardinalityNextOld, observationCardinalityNextNew + ); + } + } + + /// @inheritdoc IUniswapV3PoolActions + /// @dev not locked because it initializes unlocked + function initialize(uint160 sqrtPriceX96) external override { + require(slot0.sqrtPriceX96 == 0, "AI"); + + int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); + + (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp()); + + slot0 = Slot0({ + sqrtPriceX96: sqrtPriceX96, + tick: tick, + observationIndex: 0, + observationCardinality: cardinality, + observationCardinalityNext: cardinalityNext, + feeProtocol: 0, + unlocked: true + }); + + emit Initialize(sqrtPriceX96, tick); + } + + struct ModifyPositionParams { + // the address that owns the position + address owner; + // the lower and upper tick of the position + int24 tickLower; + int24 tickUpper; + // any change in liquidity + int128 liquidityDelta; + } + + /// @dev Effect some changes to a position + /// @param params the position details and the change to the position's liquidity to effect + /// @return position a storage pointer referencing the position with the given owner and tick range + /// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient + /// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient + function _modifyPosition(ModifyPositionParams memory params) + private + noDelegateCall + returns (Position.Info storage position, int256 amount0, int256 amount1) + { + checkTicks(params.tickLower, params.tickUpper); + + Slot0 memory _slot0 = slot0; // SLOAD for gas optimization + + position = _updatePosition( + params.owner, params.tickLower, params.tickUpper, params.liquidityDelta, _slot0.tick + ); + + if (params.liquidityDelta != 0) { + if (_slot0.tick < params.tickLower) { + // current tick is below the passed range; liquidity can only become in range by crossing from left to + // right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it + amount0 = SqrtPriceMath.getAmount0Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + } else if (_slot0.tick < params.tickUpper) { + // current tick is inside the passed range + uint128 liquidityBefore = liquidity; // SLOAD for gas optimization + + // write an oracle entry + (slot0.observationIndex, slot0.observationCardinality) = observations.write( + _slot0.observationIndex, + _blockTimestamp(), + _slot0.tick, + liquidityBefore, + _slot0.observationCardinality, + _slot0.observationCardinalityNext + ); + + amount0 = SqrtPriceMath.getAmount0Delta( + _slot0.sqrtPriceX96, + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + amount1 = SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + _slot0.sqrtPriceX96, + params.liquidityDelta + ); + + liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta); + } else { + // current tick is above the passed range; liquidity can only become in range by crossing from right to + // left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it + amount1 = SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + } + } + } + + /// @dev Gets and updates a position with the given liquidity delta + /// @param owner the owner of the position + /// @param tickLower the lower tick of the position's tick range + /// @param tickUpper the upper tick of the position's tick range + /// @param tick the current tick, passed to avoid sloads + function _updatePosition( + address owner, + int24 tickLower, + int24 tickUpper, + int128 liquidityDelta, + int24 tick + ) private returns (Position.Info storage position) { + position = positions.get(owner, tickLower, tickUpper); + + uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization + uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization + + // if we need to update the ticks, do it + bool flippedLower; + bool flippedUpper; + if (liquidityDelta != 0) { + uint32 time = _blockTimestamp(); + (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations + .observeSingle( + time, 0, slot0.tick, slot0.observationIndex, liquidity, slot0.observationCardinality + ); + + flippedLower = ticks.update( + tickLower, + tick, + liquidityDelta, + _feeGrowthGlobal0X128, + _feeGrowthGlobal1X128, + secondsPerLiquidityCumulativeX128, + tickCumulative, + time, + false, + maxLiquidityPerTick + ); + flippedUpper = ticks.update( + tickUpper, + tick, + liquidityDelta, + _feeGrowthGlobal0X128, + _feeGrowthGlobal1X128, + secondsPerLiquidityCumulativeX128, + tickCumulative, + time, + true, + maxLiquidityPerTick + ); + + if (flippedLower) { + tickBitmap.flipTick(tickLower, tickSpacing); + } + if (flippedUpper) { + tickBitmap.flipTick(tickUpper, tickSpacing); + } + } + + (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getFeeGrowthInside( + tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128 + ); + + position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); + + // clear any tick data that is no longer needed + if (liquidityDelta < 0) { + if (flippedLower) { + ticks.clear(tickLower); + } + if (flippedUpper) { + ticks.clear(tickUpper); + } + } + } + + /// @inheritdoc IUniswapV3PoolActions + /// @dev noDelegateCall is applied indirectly via _modifyPosition + function mint( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount, + bytes calldata data + ) external override lock returns (uint256 amount0, uint256 amount1) { + require(amount > 0); + (, int256 amount0Int, int256 amount1Int) = _modifyPosition( + ModifyPositionParams({ + owner: recipient, + tickLower: tickLower, + tickUpper: tickUpper, + liquidityDelta: int256(uint256(amount)).toInt128() + }) + ); + + amount0 = uint256(amount0Int); + amount1 = uint256(amount1Int); + + uint256 balance0Before; + uint256 balance1Before; + if (amount0 > 0) balance0Before = balance0(); + if (amount1 > 0) balance1Before = balance1(); + IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data); + if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), "M0"); + if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), "M1"); + + emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1); + } + + /// @inheritdoc IUniswapV3PoolActions + function collect( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount0Requested, + uint128 amount1Requested + ) external override lock returns (uint128 amount0, uint128 amount1) { + // we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1} + Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper); + + amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested; + amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested; + + if (amount0 > 0) { + position.tokensOwed0 -= amount0; + TransferHelper.safeTransfer(token0, recipient, amount0); + } + if (amount1 > 0) { + position.tokensOwed1 -= amount1; + TransferHelper.safeTransfer(token1, recipient, amount1); + } + + emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1); + } + + /// @inheritdoc IUniswapV3PoolActions + /// @dev noDelegateCall is applied indirectly via _modifyPosition + function burn(int24 tickLower, int24 tickUpper, uint128 amount) + external + override + lock + returns (uint256 amount0, uint256 amount1) + { + (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( + ModifyPositionParams({ + owner: msg.sender, + tickLower: tickLower, + tickUpper: tickUpper, + liquidityDelta: -int256(uint256(amount)).toInt128() + }) + ); + + amount0 = uint256(-amount0Int); + amount1 = uint256(-amount1Int); + + if (amount0 > 0 || amount1 > 0) { + (position.tokensOwed0, position.tokensOwed1) = + (position.tokensOwed0 + uint128(amount0), position.tokensOwed1 + uint128(amount1)); + } + + emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1); + } + + struct SwapCache { + // the protocol fee for the input token + uint8 feeProtocol; + // liquidity at the beginning of the swap + uint128 liquidityStart; + // the timestamp of the current block + uint32 blockTimestamp; + // the current value of the tick accumulator, computed only if we cross an initialized tick + int56 tickCumulative; + // the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick + uint160 secondsPerLiquidityCumulativeX128; + // whether we've computed and cached the above two accumulators + bool computedLatestObservation; + } + + // the top level state of the swap, the results of which are recorded in storage at the end + struct SwapState { + // the amount remaining to be swapped in/out of the input/output asset + int256 amountSpecifiedRemaining; + // the amount already swapped out/in of the output/input asset + int256 amountCalculated; + // current sqrt(price) + uint160 sqrtPriceX96; + // the tick associated with the current price + int24 tick; + // the global fee growth of the input token + uint256 feeGrowthGlobalX128; + // amount of input token paid as protocol fee + uint128 protocolFee; + // the current liquidity in range + uint128 liquidity; + } + + struct StepComputations { + // the price at the beginning of the step + uint160 sqrtPriceStartX96; + // the next tick to swap to from the current tick in the swap direction + int24 tickNext; + // whether tickNext is initialized or not + bool initialized; + // sqrt(price) for the next tick (1/0) + uint160 sqrtPriceNextX96; + // how much is being swapped in in this step + uint256 amountIn; + // how much is being swapped out + uint256 amountOut; + // how much fee is being paid in + uint256 feeAmount; + } + + /// @inheritdoc IUniswapV3PoolActions + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external override noDelegateCall returns (int256 amount0, int256 amount1) { + require(amountSpecified != 0, "AS"); + + Slot0 memory slot0Start = slot0; + + require(slot0Start.unlocked, "LOK"); + require( + zeroForOne + ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 + && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO + : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 + && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, + "SPL" + ); + + slot0.unlocked = false; + + SwapCache memory cache = SwapCache({ + liquidityStart: liquidity, + blockTimestamp: _blockTimestamp(), + feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), + secondsPerLiquidityCumulativeX128: 0, + tickCumulative: 0, + computedLatestObservation: false + }); + + bool exactInput = amountSpecified > 0; + + SwapState memory state = SwapState({ + amountSpecifiedRemaining: amountSpecified, + amountCalculated: 0, + sqrtPriceX96: slot0Start.sqrtPriceX96, + tick: slot0Start.tick, + feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128, + protocolFee: 0, + liquidity: cache.liquidityStart + }); + + // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit + while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) { + StepComputations memory step; + + step.sqrtPriceStartX96 = state.sqrtPriceX96; + + (step.tickNext, step.initialized) = + tickBitmap.nextInitializedTickWithinOneWord(state.tick, tickSpacing, zeroForOne); + + // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds + if (step.tickNext < TickMath.MIN_TICK) { + step.tickNext = TickMath.MIN_TICK; + } else if (step.tickNext > TickMath.MAX_TICK) { + step.tickNext = TickMath.MAX_TICK; + } + + // get the price for the next tick + step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext); + + // compute values to swap to the target tick, price limit, or point where input/output amount is exhausted + (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath + .computeSwapStep( + state.sqrtPriceX96, + ( + zeroForOne + ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 + : step.sqrtPriceNextX96 > sqrtPriceLimitX96 + ) ? sqrtPriceLimitX96 : step.sqrtPriceNextX96, + state.liquidity, + state.amountSpecifiedRemaining, + fee + ); + + if (exactInput) { + state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); + state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256()); + } else { + state.amountSpecifiedRemaining += step.amountOut.toInt256(); + state.amountCalculated = + state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256()); + } + + // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee + if (cache.feeProtocol > 0) { + uint256 delta = step.feeAmount / cache.feeProtocol; + step.feeAmount -= delta; + state.protocolFee += uint128(delta); + } + + // update global fee tracker + if (state.liquidity > 0) { + state.feeGrowthGlobalX128 += + FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity); + } + + // shift tick if we reached the next price + if (state.sqrtPriceX96 == step.sqrtPriceNextX96) { + // if the tick is initialized, run the tick transition + if (step.initialized) { + // check for the placeholder value, which we replace with the actual value the first time the swap + // crosses an initialized tick + if (!cache.computedLatestObservation) { + (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = + observations.observeSingle( + cache.blockTimestamp, + 0, + slot0Start.tick, + slot0Start.observationIndex, + cache.liquidityStart, + slot0Start.observationCardinality + ); + cache.computedLatestObservation = true; + } + int128 liquidityNet = ticks.cross( + step.tickNext, + (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128), + (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), + cache.secondsPerLiquidityCumulativeX128, + cache.tickCumulative, + cache.blockTimestamp + ); + // if we're moving leftward, we interpret liquidityNet as the opposite sign + // safe because liquidityNet cannot be type(int128).min + if (zeroForOne) liquidityNet = -liquidityNet; + + state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet); + } + + state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext; + } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) { + // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved + state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96); + } + } + + // update tick and write an oracle entry if the tick change + if (state.tick != slot0Start.tick) { + (uint16 observationIndex, uint16 observationCardinality) = observations.write( + slot0Start.observationIndex, + cache.blockTimestamp, + slot0Start.tick, + cache.liquidityStart, + slot0Start.observationCardinality, + slot0Start.observationCardinalityNext + ); + (slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = + (state.sqrtPriceX96, state.tick, observationIndex, observationCardinality); + } else { + // otherwise just update the price + slot0.sqrtPriceX96 = state.sqrtPriceX96; + } + + // update liquidity if it changed + if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity; + + // update fee growth global and, if necessary, protocol fees + // overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees + if (zeroForOne) { + feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; + if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; + } else { + feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; + if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; + } + + (amount0, amount1) = zeroForOne == exactInput + ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) + : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining); + + // do the transfers and collect payment + if (zeroForOne) { + if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); + + uint256 balance0Before = balance0(); + IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); + require(balance0Before.add(uint256(amount0)) <= balance0(), "IIA"); + } else { + if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); + + uint256 balance1Before = balance1(); + IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); + require(balance1Before.add(uint256(amount1)) <= balance1(), "IIA"); + } + + emit Swap( + msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick + ); + slot0.unlocked = true; + } + + /// @inheritdoc IUniswapV3PoolActions + function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) + external + override + lock + noDelegateCall + { + uint128 _liquidity = liquidity; + require(_liquidity > 0, "L"); + + uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6); + uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6); + uint256 balance0Before = balance0(); + uint256 balance1Before = balance1(); + + if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0); + if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1); + + IUniswapV3FlashCallback(msg.sender).uniswapV3FlashCallback(fee0, fee1, data); + + uint256 balance0After = balance0(); + uint256 balance1After = balance1(); + + require(balance0Before.add(fee0) <= balance0After, "F0"); + require(balance1Before.add(fee1) <= balance1After, "F1"); + + // sub is safe because we know balanceAfter is gt balanceBefore by at least fee + uint256 paid0 = balance0After - balance0Before; + uint256 paid1 = balance1After - balance1Before; + + if (paid0 > 0) { + uint8 feeProtocol0 = slot0.feeProtocol % 16; + uint256 fees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0; + if (uint128(fees0) > 0) protocolFees.token0 += uint128(fees0); + feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - fees0, FixedPoint128.Q128, _liquidity); + } + if (paid1 > 0) { + uint8 feeProtocol1 = slot0.feeProtocol >> 4; + uint256 fees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1; + if (uint128(fees1) > 0) protocolFees.token1 += uint128(fees1); + feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - fees1, FixedPoint128.Q128, _liquidity); + } + + emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1); + } + + /// @inheritdoc IUniswapV3PoolOwnerActions + function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) + external + override + lock + onlyFactoryOwner + { + require( + (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) + && (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)) + ); + uint8 feeProtocolOld = slot0.feeProtocol; + slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); + emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1); + } + + /// @inheritdoc IUniswapV3PoolOwnerActions + function collectProtocol(address recipient, uint128 amount0Requested, uint128 amount1Requested) + external + override + lock + onlyFactoryOwner + returns (uint128 amount0, uint128 amount1) + { + amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested; + amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested; + + if (amount0 > 0) { + if (amount0 == protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings + protocolFees.token0 -= amount0; + TransferHelper.safeTransfer(token0, recipient, amount0); + } + if (amount1 > 0) { + if (amount1 == protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings + protocolFees.token1 -= amount1; + TransferHelper.safeTransfer(token1, recipient, amount1); + } + + emit CollectProtocol(msg.sender, recipient, amount0, amount1); + } +}